<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta property="og:type" content="website">
<meta property="og:title" content="Technological Blog">
<meta property="og:url" content="http://example.com/page/5/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Li Yudong">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://example.com/page/5/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/09/06/%E7%AE%97%E6%B3%95/JS%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%EF%BC%88%E4%BA%8C%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/09/06/%E7%AE%97%E6%B3%95/JS%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%EF%BC%88%E4%BA%8C%EF%BC%89/" class="post-title-link" itemprop="url">JS力扣刷题记（二）</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-09-06 16:12:42" itemprop="dateCreated datePublished" datetime="2021-09-06T16:12:42+08:00">2021-09-06</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-09-30 12:33:39" itemprop="dateModified" datetime="2021-09-30T12:33:39+08:00">2021-09-30</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h2 id="21、合并升序链表"><a href="#21、合并升序链表" class="headerlink" title="21、合并升序链表"></a>21、合并升序链表</h2><p>题目：将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 </p>
<p>思路：同样也是很简单直观的思路，一个个比较然后加上即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * function ListNode(val, next) &#123;</span></span><br><span class="line"><span class="comment"> *     this.val = (val===undefined ? 0 : val)</span></span><br><span class="line"><span class="comment"> *     this.next = (next===undefined ? null : next)</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l1</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l2</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;ListNode&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> mergeTwoLists = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ans = <span class="keyword">new</span> ListNode();</span><br><span class="line">    <span class="keyword">let</span> left = ans;</span><br><span class="line">    <span class="keyword">while</span> (l1 !== <span class="literal">null</span> || l2 !== <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (l1 === <span class="literal">null</span>) &#123;</span><br><span class="line">            left.next = l2;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (l2 === <span class="literal">null</span>) &#123;</span><br><span class="line">            left.next = l1;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (l1.val &lt; l2.val) &#123;</span><br><span class="line">            left.next = l1;</span><br><span class="line">            l1 = l1.next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            left.next = l2;</span><br><span class="line">            l2 = l2.next;</span><br><span class="line">        &#125;</span><br><span class="line">        left = left.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans.next</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="22、括号生成"><a href="#22、括号生成" class="headerlink" title="22、括号生成"></a>22、括号生成</h2><p>题目：数字 n 代表生成括号的对数，请你设计一个函数，用于能够生成所有可能的并且 有效的 括号组合。</p>
<p>思路：这种问题其实还是多叉决策树的遍历问题，基本有DFS与BFS两种解决方案，DFS一般利用递归，用借助系统栈来实现状态的转移，而BFS需自己编写结点类与队列。同样，根据题设条件：树的剪枝逻辑如下：</p>
<p>1、当前左右括号都有大于 0 个可以使用的时候，才产生分支；</p>
<p>2、产生左分支的时候，只看当前是否还有左括号可以使用；</p>
<p>3、产生右分支的时候，还受到左分支的限制，右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候，才可以产生分支；</p>
<p>4、在左边和右边剩余的括号数都等于 0 的时候结算。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> generateParenthesis = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> res = [];</span><br><span class="line">    <span class="comment">//根据上面分析可知，每次处理、剪枝需要考虑剩余的左右括号的个数，因此递归的传递函数中除了修改的目标str，</span></span><br><span class="line">    <span class="comment">//还需要传入左右括号的剩余个数</span></span><br><span class="line">    <span class="keyword">let</span> generate = <span class="function">(<span class="params">str, left, right</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (left === <span class="number">0</span> &amp;&amp; right ===<span class="number">0</span>) &#123;</span><br><span class="line">            res.push(str);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (left &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            generate(str + <span class="string">'('</span>, left - <span class="number">1</span>, right);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (right &gt; left) &#123;</span><br><span class="line">            generate(str + <span class="string">')'</span>, left, right - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    generate(<span class="string">""</span>, n, n);</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*用递归来处理明显比用构建栈、队列处理要方便地多</span></span><br><span class="line"><span class="comment">跟递归需要传递的参数一样，这一逻辑在判断能够入队列时，取决于左右括号的剩余数量，</span></span><br><span class="line"><span class="comment">因此，在队列中保存的值，除了当前的字符串str，同样需要需要有剩下的左右括号个数</span></span><br><span class="line"><span class="comment">这样可以用一个三元数组来存储，或者自定义一个类来存储</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> generateParenthesis = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ans = [];</span><br><span class="line">    <span class="keyword">let</span> queue = [];</span><br><span class="line">    queue.push([n,n,<span class="string">''</span>]);</span><br><span class="line">    <span class="keyword">while</span>(queue.length) &#123;</span><br><span class="line">        <span class="keyword">let</span> node = queue.shift();</span><br><span class="line">        <span class="keyword">const</span> left = node[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">const</span> right = node[<span class="number">1</span>];</span><br><span class="line">        <span class="keyword">const</span> res = node[<span class="number">2</span>];</span><br><span class="line">        <span class="keyword">if</span>(left === n &amp;&amp; right === n) ans.push(res);</span><br><span class="line">        <span class="keyword">if</span>(left &lt; n) queue.push([left + <span class="number">1</span>,right,res+<span class="string">"("</span>]);</span><br><span class="line">        <span class="keyword">if</span>(right &lt; n &amp;&amp; left &gt; right) queue.push([left,right+<span class="number">1</span>,res+<span class="string">")"</span>])</span><br><span class="line">        <span class="comment">//根据三种不同的情况来判断结果队列中数据的处理方式，</span></span><br><span class="line">        <span class="comment">//每次均出列一个数进行处理，处理结束后入列，</span></span><br><span class="line">        <span class="comment">//并根据长度判断何时永久退出队列</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//同样，可以构建一个类来代替三元数组进行储存</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Node</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(L,R,res) &#123;</span><br><span class="line">        <span class="keyword">this</span>.left = L;</span><br><span class="line">        <span class="keyword">this</span>.right = R;</span><br><span class="line">        <span class="keyword">this</span>.res = res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> generateParenthesis = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ans = [];</span><br><span class="line">    <span class="keyword">let</span> queue = [];</span><br><span class="line">    queue.push(<span class="keyword">new</span> Node(<span class="number">0</span>,<span class="number">0</span>,<span class="string">''</span>));</span><br><span class="line">    <span class="keyword">while</span>(queue.length) &#123;</span><br><span class="line">        <span class="keyword">let</span> node = queue.shift();</span><br><span class="line">        <span class="keyword">const</span> &#123;left,right,res&#125; = node;</span><br><span class="line">        <span class="keyword">if</span>(left === n &amp;&amp; right === n) ans.push(res);</span><br><span class="line">        <span class="keyword">if</span>(left &lt; n) queue.push(<span class="keyword">new</span> Node(left + <span class="number">1</span>,right,res+<span class="string">"("</span>));</span><br><span class="line">        <span class="keyword">if</span>(right &lt; n &amp;&amp; left &gt; right) queue.push(<span class="keyword">new</span> Node(left,right+<span class="number">1</span>,res+<span class="string">")"</span>))</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="23、合并K个升序链表"><a href="#23、合并K个升序链表" class="headerlink" title="23、合并K个升序链表"></a>23、合并K个升序链表</h2><p>题目：给你一个链表数组，每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中，返回合并后的链表。 </p>
<p>思路：基本上有3种解决方法：暴力解法、递归分治、优先队列。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mergeKLists = <span class="function"><span class="keyword">function</span>(<span class="params">lists</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> mergeTwoLists = <span class="function">(<span class="params">l1,l2</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> preHead = <span class="keyword">new</span> ListNode(<span class="number">-1</span>)</span><br><span class="line">      <span class="keyword">let</span> preNode = preHead</span><br><span class="line">      <span class="keyword">while</span>(l1 &amp;&amp; l2)&#123;</span><br><span class="line">          <span class="keyword">if</span>(l1.val &lt;= l2.val)&#123;</span><br><span class="line">              preNode.next = l1</span><br><span class="line">              l1 = l1.next</span><br><span class="line">          &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">              preNode.next = l2</span><br><span class="line">              l2 = l2.next</span><br><span class="line">          &#125;</span><br><span class="line">          preNode = preNode.next</span><br><span class="line">      &#125;</span><br><span class="line">      preNode.next = l1 ? l1 : l2</span><br><span class="line">      <span class="keyword">return</span> preHead.next</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">let</span> n = lists.length</span><br><span class="line">  <span class="keyword">if</span>(n == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line">  <span class="keyword">let</span> res = lists[<span class="number">0</span>]</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">1</span>;i &lt; n;i++)&#123;</span><br><span class="line">      <span class="keyword">if</span>(lists[i])&#123;</span><br><span class="line">          res = mergeTwoLists(res,lists[i])</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> res</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="24、两两交换链表中节点"><a href="#24、两两交换链表中节点" class="headerlink" title="24、两两交换链表中节点"></a>24、两两交换链表中节点</h2><p>题目：给定一个链表，两两交换其中相邻的节点，并返回交换后的链表。你不能只是单纯的改变节点内部的值，而是需要实际的进行节点交换。</p>
<p>思路：最容易想到的就是迭代方法，从头往后一步步地交换或者使用递归能够让代码更加简洁</p>
<p>1、递归的基本概念:程序调用自身的编程技巧称为递归,是函数自己调用自己.一个函数在其定义中直接或间接调用自身的一种方法,它通常把一个大型的复杂的问题转化为一个与原问题相似的规模较小的问题来解决,可以极大的减少代码量.递归的能力在于用有限的语句来定义对象的无限集合。</p>
<p>2、迭代:利用变量的原值推算出变量的一个新值.如果递归是du自己调用自己的话,迭代就是A不停的调用B。</p>
<p>3、递归中一定有迭代,但是迭代中不一定有递归,大部分可以相互转换.能用迭代的不用递归,递归调用函数,浪费空间,并且递归太深容易造成堆栈的溢出。</p>
<p>递归的关键：递归关系与递归终止条件。（其他的扔给递归）1、找整个递归的终止条件：递归应该在什么时候结束？2、找返回值：应该给上一级返回什么信息？3、本级递归应该做什么：在这一级递归中，应该完成什么任务？</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//递归的方法如下：</span></span><br><span class="line"><span class="keyword">var</span> swapPairs = <span class="function"><span class="keyword">function</span>(<span class="params">head</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (head === <span class="literal">null</span>|| head.next === <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> head;<span class="comment">//先定义递归的出口</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> newHead = head.next;</span><br><span class="line">    head.next = swapPairs(newHead.next);</span><br><span class="line">    newHead.next = head;</span><br><span class="line">    <span class="keyword">return</span> newHead;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//迭代的方法如下：</span></span><br><span class="line"><span class="keyword">var</span> swapPairs = <span class="function"><span class="keyword">function</span>(<span class="params">head</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> dummyHead = <span class="keyword">new</span> ListNode(<span class="number">0</span>);</span><br><span class="line">    dummyHead.next = head;</span><br><span class="line">    <span class="keyword">let</span> temp = dummyHead;</span><br><span class="line">    <span class="keyword">while</span> (temp.next !== <span class="literal">null</span> &amp;&amp; temp.next.next !== <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> node1 = temp.next;</span><br><span class="line">        <span class="keyword">const</span> node2 = temp.next.next;</span><br><span class="line">        temp.next = node2;</span><br><span class="line">        node1.next = node2.next;</span><br><span class="line">        node2.next = node1;</span><br><span class="line">        temp = node1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dummyHead.next;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="25、k个一组翻转链表"><a href="#25、k个一组翻转链表" class="headerlink" title="25、k个一组翻转链表"></a>25、k个一组翻转链表</h2><p>题目：给你一个链表，每 k 个节点一组进行翻转，请你返回翻转后的链表。k 是一个正整数，它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍，那么请将最后剩余的节点保持原有顺序。</p>
<p>思路：本题的目标非常清晰易懂，不涉及复杂的算法，但是实现过程中需要考虑的细节比较多，容易写出冗长的代码。主要考察面试者设计的能力。我们需要把链表结点按照 k 个一组分组，所以可以使用一个指针 head 依次指向每组的头结点。这个指针每次向前移动 k 步，直至链表结尾。对于每个分组，我们先判断它的长度是否大于等于 k。若是，我们就翻转这部分链表，否则不需要翻转。接下来的问题就是如何翻转一个分组内的子链表。翻转一个链表并不难，过程可以参考反转链表。但是对于一个子链表，除了翻转其本身之外，还需要将子链表的头部与上一个子链表连接，以及子链表的尾部与下一个子链表连接。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myReverse = <span class="function">(<span class="params">head, tail</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> prev = <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">let</span> p = head;</span><br><span class="line">    <span class="keyword">while</span> (prev !== tail) &#123;</span><br><span class="line">        <span class="keyword">const</span> nex = p.next;</span><br><span class="line">        p.next = prev;</span><br><span class="line">        prev = p;</span><br><span class="line">        p = nex;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> [tail, head];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> reverseKGroup = <span class="function"><span class="keyword">function</span>(<span class="params">head, k</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> hair = <span class="keyword">new</span> ListNode(<span class="number">0</span>);</span><br><span class="line">    hair.next = head;</span><br><span class="line">    <span class="keyword">let</span> pre = hair;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (head) &#123;</span><br><span class="line">        <span class="keyword">let</span> tail = pre;</span><br><span class="line">        <span class="comment">// 查看剩余部分长度是否大于等于 k</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; k; ++i) &#123;</span><br><span class="line">            tail = tail.next;</span><br><span class="line">            <span class="keyword">if</span> (!tail) &#123;</span><br><span class="line">                <span class="keyword">return</span> hair.next;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">const</span> nex = tail.next;</span><br><span class="line">        [head, tail] = myReverse(head, tail);</span><br><span class="line">        <span class="comment">// 把子链表重新接回原链表</span></span><br><span class="line">        pre.next = head;</span><br><span class="line">        tail.next = nex;</span><br><span class="line">        pre = tail;</span><br><span class="line">        head = tail.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> hair.next;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="26、删除排序数组中的重复项"><a href="#26、删除排序数组中的重复项" class="headerlink" title="26、删除排序数组中的重复项"></a>26、删除排序数组中的重复项</h2><p>题目：给定一个排序数组，你需要在 原地 删除重复出现的元素，使得每个元素只出现一次，返回移除后数组的新长度。不要使用额外的数组空间，你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。</p>
<p>思路：如果没有不使用额外空间的话，之间新建一个hash表来进行比对即可。若是数组为乱序，可以先将其排序，然后对有数组双指针进行搜索，最开始的时候,两个指针都指向第一个数字，如果两个指针指向的数字相同，则快指针向前走一步,如果不同，则两个指针都向前走一步，这样当快指针走完整个数组后,慢指针当前的坐标+1就是数组中不同数字的个数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> removeDuplicates = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> left = <span class="number">0</span>, right = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(right &lt; nums.length)&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums[left] !== nums[right])&#123;</span><br><span class="line">            left = left + <span class="number">1</span>;</span><br><span class="line">            nums[left] = nums[right];</span><br><span class="line">        &#125;</span><br><span class="line">        right++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> left + <span class="number">1</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="27、移除元素"><a href="#27、移除元素" class="headerlink" title="27、移除元素"></a>27、移除元素</h2><p>题目：给你一个数组 nums 和一个值 val，你需要 原地 移除所有数值等于 val 的元素，并返回移除后数组的新长度。不要使用额外的数组空间，你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> removeElement = <span class="function"><span class="keyword">function</span>(<span class="params">nums, val</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ind = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] != val)&#123;</span><br><span class="line">            nums[ind++] = nums[i];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ind;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="28、实现strSTR-函数"><a href="#28、实现strSTR-函数" class="headerlink" title="28、实现strSTR()函数"></a>28、实现strSTR()函数</h2><p>题目：实现字符串查找函数；给定一个 haystack 字符串和一个 needle 字符串，在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在，则返回 -1。当 needle 是空字符串时，我们应当返回什么值呢？这是一个在面试中很好的问题。对于本题而言，当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//方法一、直接使用indexOf()即可</span></span><br><span class="line"><span class="keyword">var</span> strStr = <span class="function"><span class="keyword">function</span>(<span class="params">haystack, needle</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> haystack.indexOf(needle)</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//方法二、两层for循环来进行遍历查找</span></span><br><span class="line"><span class="keyword">var</span> strStr = <span class="function"><span class="keyword">function</span>(<span class="params">haystack, needle</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (needle===<span class="string">""</span>) <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>;i&lt;haystack.length;i++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(haystack[i]===needle[<span class="number">0</span>])&#123;</span><br><span class="line">            <span class="keyword">var</span> flag = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">var</span> j=<span class="number">1</span>;j&lt;needle.length;j++)&#123;</span><br><span class="line">                <span class="keyword">if</span> (haystack[i+j]!=needle[j])&#123;</span><br><span class="line">                    flag = <span class="literal">false</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (flag) <span class="keyword">return</span> i</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//方法三、字符串匹配的KMP算法</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">核心思想：当出现字符串不匹配时，可以知道一部分之前已经匹配的文本内容，可以利用这些信息避免从头再去做匹配了。</span></span><br><span class="line"><span class="comment">所以如何记录已经匹配的文本内容，是KMP的重点，也是next数组肩负的重任。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">当不匹配时，寻找已经匹配的部分中有哪些是重复的，即后缀与前缀相同。若有则将前缀移到对应的后缀位置，并从此开始来继续一步步匹配，</span></span><br><span class="line"><span class="comment">即在失去匹配时，操作模式串，从而找到其最长的前缀并匹配。所以，提前操作模式串，生成next[]数组。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> strStr = <span class="function"><span class="keyword">function</span>(<span class="params">haystack, needle</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(needle == <span class="string">""</span>)<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> next = getNext(needle);</span><br><span class="line">    <span class="keyword">let</span> hi = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> ne = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(hi&lt;haystack.length)&#123;</span><br><span class="line">        <span class="keyword">if</span>(ne == <span class="number">-1</span> || haystack[hi] == needle[ne])&#123;<span class="comment">//相等情况</span></span><br><span class="line">            <span class="keyword">if</span>(ne == needle.length - <span class="number">1</span>) <span class="keyword">return</span> (hi - needle.length + <span class="number">1</span>);</span><br><span class="line">            hi++;</span><br><span class="line">            ne++;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;<span class="comment">//失配情况</span></span><br><span class="line">            ne = next[ne];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getNext</span>(<span class="params">needle</span>)</span>&#123;<span class="comment">//获取next数组</span></span><br><span class="line">    <span class="keyword">let</span> next = [];</span><br><span class="line">    next[<span class="number">0</span>] = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> j = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">while</span>(i &lt; needle.length)&#123;</span><br><span class="line">        <span class="keyword">if</span>(j == <span class="number">-1</span> || needle[i] == needle[j])&#123;</span><br><span class="line">            i = i + <span class="number">1</span>;</span><br><span class="line">            j = j + <span class="number">1</span>;</span><br><span class="line">            next[i] = j;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            j = next[j]<span class="comment">//回溯</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="29、两数相除"><a href="#29、两数相除" class="headerlink" title="29、两数相除"></a>29、两数相除</h2><p>题目：给定两个整数，被除数 dividend 和除数 divisor。将两数相除，要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。整数除法的结果应当截去（truncate）其小数部分，例如：truncate(8.345) = 8 以及 truncate(-2.7335) = -2。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="30、串联所有单词的字串"><a href="#30、串联所有单词的字串" class="headerlink" title="30、串联所有单词的字串"></a>30、串联所有单词的字串</h2><p>题目：给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。</p>
<p>注意子串要与 words 中的单词完全匹配，中间不能有其他字符 ，但不需要考虑 words 中单词串联的顺序。</p>
<p>示例 1：</p>
<p>输入：s = “barfoothefoobarman”, words = [“foo”,”bar”]<br>输出：[0,9]<br>解释：<br>从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。<br>输出的顺序不重要, [9,0] 也是有效答案。<br>示例 2：</p>
<p>输入：s = “wordgoodgoodgoodbestword”, words = [“word”,”good”,”best”,”word”]<br>输出：[]<br>示例 3：</p>
<p>输入：s = “barfoofoobarthefoobarman”, words = [“bar”,”foo”,”the”]<br>输出：[6,9,12]</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="31、下一个排列"><a href="#31、下一个排列" class="headerlink" title="31、下一个排列"></a>31、下一个排列</h2><p>题目：实现获取下一个排列的函数，算法需要将给定数字序列重新排列成字典序中下一个更大的排列。</p>
<p>如果不存在下一个更大的排列，则将数字重新排列成最小的排列（即升序排列）。</p>
<p>必须原地修改，只允许使用额外常数空间。以下是一些例子，输入位于左侧列，其相应输出位于右侧列。</p>
<p>1,2,3 → 1,3,2</p>
<p>3,2,1 → 1,2,3</p>
<p>1,1,5 → 1,5,1</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="32、最长有效括号"><a href="#32、最长有效括号" class="headerlink" title="32、最长有效括号"></a>32、最长有效括号</h2><p>题目：给你一个只包含 ‘(‘ 和 ‘)’ 的字符串，找出最长有效（格式正确且连续）括号子串的长度。</p>
<p>示例 1：</p>
<p>输入：s = “(()”<br>输出：2<br>解释：最长有效括号子串是 “()”<br>示例 2：</p>
<p>输入：s = “)()())”<br>输出：4<br>解释：最长有效括号子串是 “()()”<br>示例 3：</p>
<p>输入：s = “”<br>输出：0</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="33、搜索旋转排序数组"><a href="#33、搜索旋转排序数组" class="headerlink" title="33、搜索旋转排序数组"></a>33、搜索旋转排序数组</h2><p>整数数组 nums 按升序排列，数组中的值 互不相同 。</p>
<p>在传递给函数之前，nums 在预先未知的某个下标 k（0 &lt;= k &lt; nums.length）上进行了 旋转，使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]]（下标 从 0 开始 计数）。例如， [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。给你 旋转后 的数组 nums 和一个整数 target ，如果 nums 中存在这个目标值 target ，则返回它的下标，否则返回 -1 。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number&#125;</span> <span class="variable">target</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> 其实这个题目的思路已经很清晰了，相较于其他更加隐蔽的二分查找问题。</span></span><br><span class="line"><span class="comment"> 本题的二分查找逻辑与结局方式其实很容易就能想到，这里就赘述了，talk is cheap。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> search = <span class="function"><span class="keyword">function</span>(<span class="params">nums, target</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 时间复杂度：O(logn)</span></span><br><span class="line">  <span class="comment">// 空间复杂度：O(1)</span></span><br><span class="line">  <span class="comment">// [6,7,8,1,2,3,4,5]</span></span><br><span class="line">  <span class="keyword">let</span> start = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">let</span> end = nums.length - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> (start &lt;= end) &#123;</span><br><span class="line">    <span class="keyword">const</span> mid = start + ((end - start) &gt;&gt; <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">if</span> (nums[mid] === target) <span class="keyword">return</span> mid;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// [start, mid]有序</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ️⚠️注意这里的等号</span></span><br><span class="line">    <span class="keyword">if</span> (nums[mid] &gt;= nums[start]) &#123;</span><br><span class="line">      <span class="comment">//target 在 [start, mid] 之间</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 其实target不可能等于nums[mid]， 但是为了对称，我还是加上了等号</span></span><br><span class="line">      <span class="keyword">if</span> (target &gt;= nums[start] &amp;&amp; target &lt;= nums[mid]) &#123;</span><br><span class="line">        end = mid - <span class="number">1</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//target 不在 [start, mid] 之间</span></span><br><span class="line">        start = mid + <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// [mid, end]有序</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// target 在 [mid, end] 之间</span></span><br><span class="line">      <span class="keyword">if</span> (target &gt;= nums[mid] &amp;&amp; target &lt;= nums[end]) &#123;</span><br><span class="line">        start = mid + <span class="number">1</span>;</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// target 不在 [mid, end] 之间</span></span><br><span class="line">        end = mid - <span class="number">1</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="34、在排序数组中查找元素的第一个和最后一个位置"><a href="#34、在排序数组中查找元素的第一个和最后一个位置" class="headerlink" title="34、在排序数组中查找元素的第一个和最后一个位置"></a>34、在排序数组中查找元素的第一个和最后一个位置</h2><p>题目：给定一个按照升序排列的整数数组 nums，和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。</p>
<p>你的算法时间复杂度必须是 O(log n) 级别。如果数组中不存在目标值，返回 [-1, -1]</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//其实这个代码的逻辑跟上题如出一辙，本质上就是一个二分查找的变种。</span></span><br><span class="line"><span class="keyword">var</span> searchRange = <span class="function"><span class="keyword">function</span> (<span class="params">nums, target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> mid, midL, midR;</span><br><span class="line">    <span class="comment">// 搜索右边界</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">searchR</span>(<span class="params">left, right, target</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (left &lt;= right) &#123;</span><br><span class="line">            mid = (left + right) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> (nums[mid] &lt;= target) left = mid + <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">else</span> right = mid - <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> right</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 在区间[0, nums.length - 1]搜索target的右边界midR</span></span><br><span class="line">    midR = searchR(<span class="number">0</span>, nums.length - <span class="number">1</span>, target)</span><br><span class="line">    <span class="comment">// midR &lt; 0说明超过边界；nums[midR] !== target说明无此元素；</span></span><br><span class="line">    <span class="keyword">if</span> (midR &lt; <span class="number">0</span> || nums[midR] !== target) <span class="keyword">return</span> [<span class="number">-1</span>, <span class="number">-1</span>]</span><br><span class="line">    <span class="comment">// 在区间[0, midR - 1]搜索target - 1的右边界midL</span></span><br><span class="line">    midL = searchR(<span class="number">0</span>, midR - <span class="number">1</span>, target - <span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> [midL + <span class="number">1</span>, midR]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="35、搜索插入位置"><a href="#35、搜索插入位置" class="headerlink" title="35、搜索插入位置"></a>35、搜索插入位置</h2><p>题目：给定一个排序数组和一个目标值，在数组中找到目标值，并返回其索引。如果目标值不存在于数组中，返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//题目本身其实就是一个最简化版的二分查找，没有太多 </span></span><br><span class="line"><span class="keyword">var</span> searchInsert = <span class="function"><span class="keyword">function</span>(<span class="params">nums, target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> n = nums.length;</span><br><span class="line">    <span class="keyword">let</span> left = <span class="number">0</span>, right = n - <span class="number">1</span>, ans = n;</span><br><span class="line">    <span class="keyword">while</span> (left &lt;= right) &#123;</span><br><span class="line">        <span class="keyword">let</span> mid = ((right - left) &gt;&gt; <span class="number">1</span>) + left;</span><br><span class="line">        <span class="keyword">if</span> (target &lt;= nums[mid]) &#123;</span><br><span class="line">            ans = mid;</span><br><span class="line">            right = mid - <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            left = mid + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="36、有效的数独"><a href="#36、有效的数独" class="headerlink" title="36、有效的数独"></a>36、有效的数独</h2><p>题目：判断一个 9x9 的数独是否有效。只需要根据以下规则，验证已经填入的数字是否有效即可。</p>
<p>数字 1-9 在每一行只能出现一次。</p>
<p>数字 1-9 在每一列只能出现一次。</p>
<p>数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。</p>
<p>上图是一个部分填充的有效的数独。</p>
<p>数独部分空格内已填入了数字，空白格用 ‘.’ 表示。</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/09/06/%E7%AE%97%E6%B3%95/JS%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/09/06/%E7%AE%97%E6%B3%95/JS%E5%8A%9B%E6%89%A3%E5%88%B7%E9%A2%98%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89/" class="post-title-link" itemprop="url">JS力扣刷题记（一）</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-09-06 16:12:42" itemprop="dateCreated datePublished" datetime="2021-09-06T16:12:42+08:00">2021-09-06</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-09-29 23:18:39" itemprop="dateModified" datetime="2021-09-29T23:18:39+08:00">2021-09-29</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如题，打算使用JS从头开始刷leetcode，大致地记录并讲解一下自己的解题思路吧，由于自己的水平原因，所以思路与解法都会比较偏新手向吧，同时不会只关注这一个题的思路，会有一些整体的思路与延伸吧。做这个记录的原因主要是想以新手的角度讲下自己解题遇到的弯路，同时也给自己加深印象吧。开始时可能注释会有些啰嗦，后续会减少不必要的代码解释。打算先写完并理解前150题吧，也并不打算分类的，因为前百题的思路都是经典解题思路，都需要慢慢体会吧。</p>
<h2 id="1、两数之和"><a href="#1、两数之和" class="headerlink" title="1、两数之和"></a>1、两数之和</h2><p>给定一个整数数组 nums 和一个整数目标值 target，请你在该数组中找出 和为目标值 的那 两个整数，并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是，数组中同一个元素不能使用两遍。你可以按任意顺序返回答案。转载自<a href="https://leetcode-cn.com/problems/two-sum/" target="_blank" rel="noopener">两数之和</a></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number&#125;</span> <span class="variable">target</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number[]&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> twoSum = <span class="function">(<span class="params">nums, target</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> res = [];</span><br><span class="line">    <span class="keyword">let</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">        map.set(nums[i], i);</span><br><span class="line">        <span class="comment">//很简单的题目与逻辑，用map即哈希表的方法辅助查找即可，使用map的set方法构建。</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> newTarget = target - nums[i];</span><br><span class="line">        <span class="keyword">if</span> (map.has(newTarget) &amp;&amp; (map.get(newTarget) !== i)) &#123;</span><br><span class="line">            res.push(i, map.get(newTarget));</span><br><span class="line">            <span class="comment">//使用has、get方法分别判断与获取索引</span></span><br><span class="line">            <span class="comment">//这里要注意下，不能两个相同索引相加</span></span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="2、两数相加"><a href="#2、两数相加" class="headerlink" title="2、两数相加"></a>2、两数相加</h2><p>给你两个非空 的链表，表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的，并且每个节点只能存储 一位 数字。请你将两个数相加，并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外，这两个数都不会以 0 开头。转载至<a href="https://leetcode-cn.com/problems/add-two-numbers/" target="_blank" rel="noopener">两数之和</a></p>
<p>例：输入：l1 = [2,4,3], l2 = [5,6,4]；输出：[7,0,8]；解释：342 + 465 = 807。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * function ListNode(val) &#123;</span></span><br><span class="line"><span class="comment"> *     this.val = val;</span></span><br><span class="line"><span class="comment"> *     this.next = null;</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l1</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l2</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;ListNode&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> addTwoNumbers = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> carry = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//定义进位，</span></span><br><span class="line">    <span class="keyword">let</span> sum = <span class="keyword">new</span> ListNode(<span class="number">0</span>);</span><br><span class="line">    <span class="comment">//一般链表题目的输出，需要先新建一个空的头节点，之后以该空结点作链表处理，从而能够避免因链表长度不够导致的报错,后续返回head.next</span></span><br><span class="line">    <span class="keyword">let</span> head = sum;</span><br><span class="line">    <span class="keyword">while</span> (l1 || l2 || carry) &#123;</span><br><span class="line">        <span class="keyword">let</span> value1 = ((l1 === <span class="literal">null</span>) ? <span class="number">0</span> : l1.val);</span><br><span class="line">        <span class="comment">//其实是在往前面补0，以确保两个加数位数一致</span></span><br><span class="line">        <span class="keyword">let</span> value2 = ((l2 === <span class="literal">null</span>) ? <span class="number">0</span> : l2.val);</span><br><span class="line">        sum.next = <span class="keyword">new</span> ListNode((value1 + value2 + carry) % <span class="number">10</span>);</span><br><span class="line">        carry = ((value1 + value2 + carry) &gt;= <span class="number">10</span> ? <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line">        sum = sum.next;</span><br><span class="line">        <span class="keyword">if</span> (l1) l1 = l1.next;</span><br><span class="line">        <span class="keyword">if</span> (l2) l2 = l2.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>一般情况下，除了用链表来存储大数以外，在JS中更常用的是用字符串来保存，因此下面我们写一个字符串类型的大数相加，基本的逻辑和链表是一样的，主要是处理的方式不同，这里我就不再赘述原理了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> add = <span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">a, b</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> m = <span class="string">""</span>;</span><br><span class="line">  <span class="keyword">let</span> n = <span class="string">""</span>;</span><br><span class="line">  <span class="keyword">let</span> res = [];</span><br><span class="line">  <span class="keyword">let</span> sum = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> (a.length &gt; b.length)&#123;</span><br><span class="line">    a = <span class="string">'0'</span> + a;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = b.length; i &lt; a.length; i++)&#123;</span><br><span class="line">      b = <span class="string">'0'</span> + b;</span><br><span class="line">    &#125;</span><br><span class="line">    m = a.split(<span class="string">''</span>);</span><br><span class="line">    n = b.split(<span class="string">''</span>);</span><br><span class="line">  &#125; </span><br><span class="line">  <span class="keyword">else</span> &#123;</span><br><span class="line">    b = <span class="string">'0'</span> + b;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = a.length; i &lt; b.length; i++)&#123;</span><br><span class="line">      a = <span class="string">'0'</span> + a;</span><br><span class="line">    &#125;</span><br><span class="line">    a = a.split(<span class="string">''</span>);</span><br><span class="line">    b = b.split(<span class="string">''</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = a.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">    <span class="keyword">let</span> k = (<span class="built_in">parseInt</span>(a[i]) + <span class="built_in">parseInt</span>(b[i]) + sum) % <span class="number">10</span>;</span><br><span class="line">        res.unshift(k);</span><br><span class="line">        <span class="keyword">if</span> ((<span class="built_in">parseInt</span>(a[i]) + <span class="built_in">parseInt</span>(b[i]) + sum) &gt;= <span class="number">10</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((m === n) &amp;&amp; (i = t)) &#123;</span><br><span class="line">                res.unshift(<span class="string">"1"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            sum = <span class="number">1</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            sum = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">let</span> ans = res.join(<span class="string">""</span>);</span><br><span class="line">  <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="3、无重复字符的最长子串"><a href="#3、无重复字符的最长子串" class="headerlink" title="3、无重复字符的最长子串"></a>3、无重复字符的最长子串</h2><p>题目：给定一个字符串，请你找出其中不含有重复字符的 <strong>最长子串</strong> 的长度。</p>
<p>例如：输入: s = “abcabcbb”。输出: 3 。解释: 因为无重复字符的最长子串是 “abc”，所以其长度为 3。</p>
<p>思路：像这种子串的问题，最小覆盖子串、字符串的排列、找到字符串中所有字母异位词、无重复字符的最长子串。其实思路基本一致，子串的问题基本都能用滑动窗口的思想来解决。滑动窗口就是双指针的进阶版吧，即维护一个窗口，不断滑动并更新答案。</p>
<p><em><strong>1、</strong></em>我们在字符串<code>S</code>中使用双指针中的左右指针技巧，初始化<code>left = right = 0</code>，把索引左闭右开区间<code>[left, right)</code>称为一个「窗口」。</p>
<p><em><strong>2、</strong></em>我们先不断地增加<code>right</code>指针扩大窗口<code>[left, right)</code>，直到窗口中的字符串符合要求（包含了<code>T</code>中的所有字符）。</p>
<p><em><strong>3、</strong></em>此时，我们停止增加<code>right</code>，转而不断增加<code>left</code>指针缩小窗口<code>[left, right)</code>，直到窗口中的字符串不再符合要求（不包含<code>T</code>中的所有字符了）。同时，每次增加<code>left</code>，我们都要更新一轮结果。</p>
<p><em><strong>4、</strong></em>重复第 2 和第 3 步，直到<code>right</code>到达字符串<code>S</code>的尽头</p>
<p><strong>第 2 步相当于在寻找一个「可行解」，然后第 3 步在优化这个「可行解」，最终找到最优解，</strong>也就是最短的覆盖子串。左右指针轮流前进，窗口大小增增减减，窗口不断向右滑动，这就是「滑动窗口」这个名字的来历。下面画图理解一下，<code>needs</code>和<code>window</code>相当于计数器，分别记录<code>T</code>中字符出现次数和「窗口」中的相应字符的出现次数。v</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">s</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> lengthOfLongestSubstring = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> right = <span class="number">0</span>, left = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> length = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> arr = [];</span><br><span class="line">    <span class="comment">//本题的关键在于如何判断是否无重复子串，用indexOf判断新进项即可，或者直接用for循环判断</span></span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.length) &#123;</span><br><span class="line">        <span class="keyword">let</span> index = arr.indexOf(s[right]);</span><br><span class="line">        <span class="keyword">if</span> (index !== <span class="number">-1</span>) &#123;</span><br><span class="line">            arr.splice(<span class="number">0</span>, index + <span class="number">1</span>);</span><br><span class="line">            <span class="comment">//将重复项之前全部删除,这里其实我们用字符串的操作来代替了滑动窗口的左移</span></span><br><span class="line">        &#125;</span><br><span class="line">        arr.push(s[right]);</span><br><span class="line">        length = <span class="built_in">Math</span>.max(length, arr.length);</span><br><span class="line">        right++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> length;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="4、寻找正序数组的两个中位数"><a href="#4、寻找正序数组的两个中位数" class="headerlink" title="4、寻找正序数组的两个中位数"></a>4、寻找正序数组的两个中位数</h2><p>题目：给定两个大小为 m 和 n 的正序（从小到大）数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。进阶：你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗？</p>
<p>思路：暴力求解的话极其简单，直接for循环即可，第一时间会想到就是归并排序最后一步。按照 O(log (m+n)) 时间复杂度的话，面对有序数组的排序，首先想到二分查找，见下方，但是对于两个数组的二分查找该如何实现。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">binary_search</span>(<span class="params">arr,low, high, key</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (low &gt; high)&#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">var</span> mid = <span class="built_in">parseInt</span>((high + low) / <span class="number">2</span>);</span><br><span class="line">            <span class="keyword">if</span>(arr[mid] == key)&#123;</span><br><span class="line">                <span class="keyword">return</span> mid;</span><br><span class="line">            &#125;<span class="keyword">else</span> <span class="keyword">if</span> (arr[mid] &gt; key)&#123;</span><br><span class="line">                high = mid - <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">return</span> binary_search(arr, low, high, key);</span><br><span class="line">            &#125;<span class="keyword">else</span> <span class="keyword">if</span> (arr[mid] &lt; key)&#123;</span><br><span class="line">                low = mid + <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">return</span> binary_search(arr, low, high, key);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br></pre></td></tr></table></figure>

<p>该题的本质可扩展为寻找两个有序数组的第k小数，不一定是中位数，因此二分查找的本质是partition，每次都剔除k/2个数，且保证这些数都在第k小数左边，即都比第k小数小，然后k = k/2；递归处理后，得到第k小数。</p>
<p>二分查找，关键点在于要partition两个排好序的数组成左右两等份，partition需要满足len(Aleft)+len(Bleft)=(m+n+1)/2 - m是数组A的长度， n是数组B的长度并且partition后 A左边最大(maxLeftA), A右边最小（minRightA), B左边最大（maxLeftB), B右边最小（minRightB) 满足(maxLeftA &lt;= minRightB &amp;&amp; maxLeftB &lt;= minRightA)有了这两个条件，那么median就在这四个数中，根据奇数或者是偶数。</p>
<p>奇数：median = max(maxLeftA, maxLeftB)；偶数：median = (max(maxLeftA, maxLeftB) + min(minRightA, minRightB)) / 2。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 二分解法</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums1</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums2</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> findMedianSortedArrays = <span class="function"><span class="keyword">function</span>(<span class="params">nums1, nums2</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 确保nums1为更短的字符串</span></span><br><span class="line">  <span class="keyword">if</span> (nums1.length &gt; nums2.length) &#123;</span><br><span class="line">    <span class="keyword">return</span> findMedianSortedArrays(nums2, nums1);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> m = nums1.length</span><br><span class="line">  <span class="keyword">const</span> n = nums2.length</span><br><span class="line">  <span class="keyword">let</span> low = <span class="number">0</span></span><br><span class="line">  <span class="keyword">let</span> high = m</span><br><span class="line">  <span class="keyword">while</span>(low &lt;= high) &#123;</span><br><span class="line">    <span class="keyword">const</span> i = low + <span class="built_in">Math</span>.floor((high - low) / <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">const</span> j = <span class="built_in">Math</span>.floor((m + n + <span class="number">1</span>) / <span class="number">2</span>) - i</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> maxLeftA = i === <span class="number">0</span> ? -<span class="literal">Infinity</span> : nums1[i<span class="number">-1</span>]</span><br><span class="line">    <span class="keyword">const</span> minRightA = i === m ? <span class="literal">Infinity</span> : nums1[i]</span><br><span class="line">    <span class="keyword">const</span> maxLeftB = j === <span class="number">0</span> ? -<span class="literal">Infinity</span> : nums2[j<span class="number">-1</span>]</span><br><span class="line">    <span class="keyword">const</span> minRightB = j === n ? <span class="literal">Infinity</span> : nums2[j]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (maxLeftA &lt;= minRightB &amp;&amp; minRightA &gt;= maxLeftB) &#123;</span><br><span class="line">      <span class="keyword">return</span> (m + n) % <span class="number">2</span> === <span class="number">1</span></span><br><span class="line">        ? <span class="built_in">Math</span>.max(maxLeftA, maxLeftB)</span><br><span class="line">        : (<span class="built_in">Math</span>.max(maxLeftA, maxLeftB) + <span class="built_in">Math</span>.min(minRightA, minRightB)) / <span class="number">2</span></span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (maxLeftA &gt; minRightB) &#123;</span><br><span class="line">      high = i - <span class="number">1</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      low = low + <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="5、最长回文子串"><a href="#5、最长回文子串" class="headerlink" title="5、最长回文子串"></a>5、最长回文子串</h2><p>题目：给你一个字符串 s，找到 s中最长的回文子串。</p>
<p>思路：最长回文子串的关键，其实通过双指针法也可以来处理，根据上题所述的思路很好进行解决，关键在于如何判定是否是回文子串的函数。但回文子串以及后序的回文子序列等问题，由于前后文联系比较紧密，且多次判断是否符合时间复杂度较大，通常使用动态规划来进行求解。</p>
<p>动态转移方程：dp[i] [j] = dp[i+1] [j-1] &amp;&amp; (dp[i] === dp[j])；dp[i] [j]为true指的是：从i到j是回文串。</p>
<p>初始条件：即字符串长度仅为0、1时，必为回文子串，由于有这样的初始条件，初始项比较多，动态转移方程需要修改成:dp[i] [j] = (dp[i] === dp[j] &amp;&amp; (j - i &lt; 2 || dp[i+1] dp[j - 1]))。同样，由于初始条件必然有j &gt; i，像这种初始条件的dp，一般都会使用斜向遍历。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">s</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;string&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> longestPalindrome = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> res = <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Array</span>(s.length), () =&gt; <span class="keyword">new</span> <span class="built_in">Array</span>(s.length).fill(<span class="number">0</span>));</span><br><span class="line">    <span class="comment">//利用Array.from构建二维数组，或者使用for循环也可</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = s.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        <span class="comment">//这里由于动态转移方程中dp[i][..]依赖于dp[i + 1][..]，因此需要倒着遍历来简化操作</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = i; j &lt; s.length; j++) &#123;</span><br><span class="line">            <span class="comment">//同样，由于初始条件原因，j从靠近i到远离i来遍历</span></span><br><span class="line">            dp[i][j] = ((s[i] === s[j]) &amp;&amp; (j - i &lt; <span class="number">2</span> || dp[i+<span class="number">1</span>][j<span class="number">-1</span>]));</span><br><span class="line">            <span class="keyword">if</span> (dp[i][j] &amp;&amp; (j - i + <span class="number">1</span> &gt; res.length)) &#123;</span><br><span class="line">                res = s.substring(i, j+<span class="number">1</span>);</span><br><span class="line">                <span class="comment">//substring截取字符串，包头不包尾</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="6、Z字形变换"><a href="#6、Z字形变换" class="headerlink" title="6、Z字形变换"></a>6、Z字形变换</h2><p>题目：将一个给定字符串 s 根据给定的行数 numRows ，以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 “PAYPALISHIRING” 行数为 3 时，排列如下：之后，你的输出需要从左往右逐行读取，产生出一个新的字符串，比如：”PAHNAPLSIIGYIR”。请你实现这个将字符串进行指定行数变换的函数：let convert(string s, int numRows)。</p>
<p> P     A     H     N<br>        A  P L S  I   I  G<br>        Y     I      R</p>
<p>思路：关键在于找规律，中间列每列一个，且列数为numsRows-2；因此将一个第一列与后续的中间列作为一个循环，个数为numsRows + numsRows - 2。这样一个循环就能找出来了，再根据每个字符在循环中的位置，分别将其置入不同行。其中仅循环的第一位为第一行，V形排列。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> convert = <span class="function"><span class="keyword">function</span>(<span class="params">s, numRows</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (numRows === <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> s;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> rows = <span class="keyword">new</span> <span class="built_in">Array</span>(numRows).fill(<span class="string">""</span>);<span class="comment">//用数组依次存储每一行的字符</span></span><br><span class="line">    <span class="keyword">let</span> circle = (<span class="number">2</span> * numRows - <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; s.length; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> x = i % circle;</span><br><span class="line">        rows[<span class="built_in">Math</span>.min(x, circle - x)] += s[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> ans = rows.join(<span class="string">""</span>);</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="7、整数反转"><a href="#7、整数反转" class="headerlink" title="7、整数反转"></a>7、整数反转</h2><p>题目：给出一个 32 位的有符号整数，你需要将这个整数中每位上的数字进行反转。</p>
<p>思路：在先判断正负号之后，用转字符串再转数组后，使用reverse()方法可以简单实现，但可以思考下用数学方法要如何处理。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先试一下数组反转，可以轻松解决</span></span><br><span class="line"><span class="keyword">let</span> reverse = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> res = x.toString().split(<span class="string">""</span>);</span><br><span class="line">    <span class="keyword">if</span> (res[<span class="number">0</span>] == <span class="string">"-"</span>) &#123;</span><br><span class="line">        res.push(<span class="string">"-"</span>);<span class="comment">//反转后，相当于在前面加-，后面的负号parseInt会忽略掉</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> ans = <span class="built_in">parseInt</span>(res.reverse().join(<span class="string">""</span>));</span><br><span class="line">    <span class="keyword">if</span> (ans &gt;= <span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">31</span>) || ans &lt;= -<span class="built_in">Math</span>.pow(<span class="number">2</span>, <span class="number">31</span>)) &#123;</span><br><span class="line">        ans = <span class="number">0</span>;</span><br><span class="line">    &#125;ue</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//再试一下数学方法，关键在于一位位地取余数</span></span><br><span class="line"><span class="keyword">let</span> reverse = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> result = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (x !== <span class="number">0</span>) &#123;</span><br><span class="line">        result = x % <span class="number">10</span> + result * <span class="number">10</span>;</span><br><span class="line">        x = (x / <span class="number">10</span>) | <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//通过位运算符保证取整(无论正负)，同时强制转换为32位有符号整数</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (result | <span class="number">0</span>) === result ? result : <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//result | 0 超过32位的整数转换结果不等于自身，可用作溢出判断。</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="8、字符串转整数"><a href="#8、字符串转整数" class="headerlink" title="8、字符串转整数"></a>8、字符串转整数</h2><p>题目：请你来实现一个 atoi 函数，使其能将字符串转换成整数。</p>
<p>首先，该函数会根据需要丢弃无用的开头空格字符，直到寻找到第一个非空格的字符为止。接下来的转化规则如下：</p>
<p>如果第一个非空字符为正或者负号时，则将该符号与之后面尽可能多的连续数字字符组合起来，形成一个有符号整数。</p>
<p>假如第一个非空字符是数字，则直接将其与之后连续的数字字符组合起来，形成一个整数。</p>
<p>该字符串在有效的整数部分之后也可能会存在多余的字符，那么这些字符可以被忽略，它们对函数不应该造成影响。</p>
<p>假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时，则你的函数不需要进行转换，即无法进行有效转换。在任何情况下，若函数不能进行有效的转换时，请返回 0 。</p>
<p>注意：本题中的空白字符只包括空格字符 ‘ ‘ 。假设我们的环境只能存储 32 位大小的有符号整数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//像这种字符串匹配的，先想到使用正则即可，先用trim来去掉前后的空格</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">s</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> myAtoi = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> re = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="regexp">/^(-|\+)?\d+/</span>);</span><br><span class="line">    <span class="keyword">let</span> str = s.trim().match(re);</span><br><span class="line">    <span class="keyword">let</span> res = str ? <span class="built_in">Number</span>(str[<span class="number">0</span>]) : <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> res &gt;= <span class="number">0</span> ? <span class="built_in">Math</span>.min(res, <span class="number">2</span>**<span class="number">31</span> - <span class="number">1</span>) : <span class="built_in">Math</span>.max(res, -(<span class="number">2</span>**<span class="number">31</span>))</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="9、回文数"><a href="#9、回文数" class="headerlink" title="9、回文数"></a>9、回文数</h2><p>题目：判断一个整数是否是回文数。回文数是指正序（从左向右）和倒序（从右向左）读都是一样的整数。</p>
<p>思路：跟前面的整数反转一样，简单的思路的话，直接变数组之后，使用reverse()方法之后判断即可；同样也有数学方法来解决这个问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//直接用reverse()</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number&#125;</span> <span class="variable">x</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;boolean&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> isPalindrome = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> x === <span class="built_in">Number</span>(x.toString().split(<span class="string">''</span>).reverse().join(<span class="string">''</span>))</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//利用数学方法来一步步求余</span></span><br><span class="line"><span class="keyword">var</span> isPalindrome = <span class="function"><span class="keyword">function</span>(<span class="params">x</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> result = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> value = x;</span><br><span class="line">    <span class="keyword">while</span> (x !== <span class="number">0</span>) &#123;</span><br><span class="line">        result = result * <span class="number">10</span> + x % <span class="number">10</span>;</span><br><span class="line">        x = (x / <span class="number">10</span>) | <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (value = result) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="10、正则表达式匹配"><a href="#10、正则表达式匹配" class="headerlink" title="10、正则表达式匹配"></a>10、正则表达式匹配</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isMatch = <span class="function">(<span class="params">s, p</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (s == <span class="literal">null</span> || p == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> sLen = s.length, pLen = p.length;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>(sLen + <span class="number">1</span>);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; dp.length; i++) &#123;</span><br><span class="line">    dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>(pLen + <span class="number">1</span>).fill(<span class="literal">false</span>); <span class="comment">// 将项默认为false</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// base case</span></span><br><span class="line">  dp[<span class="number">0</span>][<span class="number">0</span>] = <span class="literal">true</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">1</span>; j &lt; pLen + <span class="number">1</span>; j++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (p[j - <span class="number">1</span>] == <span class="string">"*"</span>) dp[<span class="number">0</span>][j] = dp[<span class="number">0</span>][j - <span class="number">2</span>];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 迭代</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; sLen + <span class="number">1</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">1</span>; j &lt; pLen + <span class="number">1</span>; j++) &#123;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (s[i - <span class="number">1</span>] == p[j - <span class="number">1</span>] || p[j - <span class="number">1</span>] == <span class="string">"."</span>) &#123;</span><br><span class="line">        dp[i][j] = dp[i - <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (p[j - <span class="number">1</span>] == <span class="string">"*"</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (s[i - <span class="number">1</span>] == p[j - <span class="number">2</span>] || p[j - <span class="number">2</span>] == <span class="string">"."</span>) &#123;</span><br><span class="line">          dp[i][j] = dp[i][j - <span class="number">2</span>] || dp[i - <span class="number">1</span>][j - <span class="number">2</span>] || dp[i - <span class="number">1</span>][j];</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          dp[i][j] = dp[i][j - <span class="number">2</span>];</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> dp[sLen][pLen]; <span class="comment">// 长sLen的s串 是否匹配 长pLen的p串</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="11、盛水最多的容器"><a href="#11、盛水最多的容器" class="headerlink" title="11、盛水最多的容器"></a>11、盛水最多的容器</h2><p>题目：给你 n 个非负整数 a1，a2，…，an，每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线，垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线，使得它们与 x 轴共同构成的容器可以容纳最多的水。说明：你不能倾斜容器，且 n 的值至少为 2。</p>
<p>示例：输入：[1,8,6,2,5,4,8,3,7]；输出：49 ；解释：图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下，容器能够容纳水（表示为蓝色部分）的最大值为 49。</p>
<p>思路：其实是一个滑动窗口(双指针)类型的题目，暴力法：即穷举所有可能，分别计算面积并保存最大值。</p>
<p>双指针法：从左右两侧开始，将较矮柱子的指针进行移动，而先不移动高柱子的指针，原因：矮柱子选取后如果移动高柱子的话面积是一定会减小的，因为长度距离在变小的时候，此时高度只能小于或等于矮的柱子。因此只能移动矮的柱子这边才有可能使得高度比矮柱子大。所以，每次都移动的是高柱子的指针。这种方法其可以看作是暴力法的剪枝，而不是传统的滑动窗口。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">height</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> maxArea = <span class="function"><span class="keyword">function</span>(<span class="params">height</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> left = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> right = height.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">let</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123;</span><br><span class="line">        <span class="keyword">let</span> height1 = <span class="built_in">Math</span>.min(height[left], height[right]);</span><br><span class="line">        <span class="keyword">let</span> aquare = height1 * (right - left);</span><br><span class="line">        ans = <span class="built_in">Math</span>.max(ans, aquare);</span><br><span class="line">        <span class="keyword">if</span> (height[left] &lt; height[right]) &#123;</span><br><span class="line">            left++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            right--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="12、整数转罗马数字"><a href="#12、整数转罗马数字" class="headerlink" title="12、整数转罗马数字"></a>12、整数转罗马数字</h2><p>思路：整数转罗马数字，比罗马数字转整数要简洁一些，同样关键在于求余的操作。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number&#125;</span> <span class="variable">num</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;string&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> intToRoman = <span class="function"><span class="keyword">function</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> Q = [<span class="string">""</span>, <span class="string">"M"</span>, <span class="string">"MM"</span>, <span class="string">"MMM"</span>];</span><br><span class="line">    <span class="keyword">var</span> B = [<span class="string">""</span>, <span class="string">"C"</span>, <span class="string">"CC"</span>, <span class="string">"CCC"</span>, <span class="string">"CD"</span>, <span class="string">"D"</span>, <span class="string">"DC"</span>, <span class="string">"DCC"</span>, <span class="string">"DCCC"</span>, <span class="string">"CM"</span>];</span><br><span class="line">    <span class="keyword">var</span> S = [<span class="string">""</span>, <span class="string">"X"</span>, <span class="string">"XX"</span>, <span class="string">"XXX"</span>, <span class="string">"XL"</span>, <span class="string">"L"</span>, <span class="string">"LX"</span>, <span class="string">"LXX"</span>, <span class="string">"LXXX"</span>, <span class="string">"XC"</span>];</span><br><span class="line">    <span class="keyword">var</span> G = [<span class="string">""</span>, <span class="string">"I"</span>, <span class="string">"II"</span>, <span class="string">"III"</span>, <span class="string">"IV"</span>, <span class="string">"V"</span>, <span class="string">"VI"</span>, <span class="string">"VII"</span>, <span class="string">"VIII"</span>, <span class="string">"IX"</span>];</span><br><span class="line">    <span class="keyword">return</span> Q[<span class="built_in">Math</span>.floor(num/<span class="number">1000</span>)] + B[<span class="built_in">Math</span>.floor((num%<span class="number">1000</span>)/<span class="number">100</span>)] + S[<span class="built_in">Math</span>.floor((num%<span class="number">100</span>)/<span class="number">10</span>)] + G[num%<span class="number">10</span>];</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//利用Math.floor来直接取整数部分，而不是四舍五入</span></span><br></pre></td></tr></table></figure>

<h2 id="13、罗马数字转整数"><a href="#13、罗马数字转整数" class="headerlink" title="13、罗马数字转整数"></a>13、罗马数字转整数</h2><p>思路：罗马数字转整数，首先将所有的组合可能性列出并添加到哈希表中</p>
<p>然后对字符串进行遍历，由于组合只有两种，一种是 1 个字符，一种是 2 个字符，其中 2 个字符优先于 1 个字符</p>
<p>先判断两个字符的组合在哈希表中是否存在，存在则将值取出加到结果 ans 中，并向后移2个字符。不存在则将判断当前 1 个字符是否存在，存在则将值取出加到结果 ans 中，并向后移 1 个字符，遍历结束返回结果 ans。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> romanToInt = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> map = &#123;</span><br><span class="line">        I : <span class="number">1</span>,</span><br><span class="line">        IV: <span class="number">4</span>,</span><br><span class="line">        V: <span class="number">5</span>,</span><br><span class="line">        IX: <span class="number">9</span>,</span><br><span class="line">        X: <span class="number">10</span>,</span><br><span class="line">        XL: <span class="number">40</span>,</span><br><span class="line">        L: <span class="number">50</span>,</span><br><span class="line">        XC: <span class="number">90</span>,</span><br><span class="line">        C: <span class="number">100</span>,</span><br><span class="line">        CD: <span class="number">400</span>,</span><br><span class="line">        D: <span class="number">500</span>,</span><br><span class="line">        CM: <span class="number">900</span>,</span><br><span class="line">        M: <span class="number">1000</span></span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">let</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>;i &lt; s.length;) &#123;</span><br><span class="line">        <span class="keyword">if</span>(i + <span class="number">1</span> &lt; s.length &amp;&amp; map[s.substring(i, i+<span class="number">2</span>)]) &#123;</span><br><span class="line">            ans += map[s.substring(i, i+<span class="number">2</span>)];</span><br><span class="line">            i += <span class="number">2</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            ans += map[s.substring(i, i+<span class="number">1</span>)];</span><br><span class="line">            i ++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="14、最长公共前缀"><a href="#14、最长公共前缀" class="headerlink" title="14、最长公共前缀"></a>14、最长公共前缀</h2><p>题目：编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀，返回空字符串 <code>&quot;&quot;</code>。</p>
<p>思路：很简单的题目，一个个的依次找最长公共前缀，先比较前两个，再用得出的公共前缀来匹配下一个；因此需要两层for循环，第一层，用于获取各个字符串，第二层对比每个字符串的各个字符。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string[]&#125;</span> <span class="variable">strs</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;string&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> longestCommonPrefix = <span class="function"><span class="keyword">function</span>(<span class="params">strs</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (strs.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> res = strs[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; strs.length; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> compare = res;</span><br><span class="line">        res = <span class="string">""</span>;</span><br><span class="line">        <span class="comment">//前两个先比较,每次比较前修改compare,并重置res</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; (j &lt; strs[i].length) &amp;&amp; (j &lt; compare.length); j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (strs[i][j] === compare[j]) &#123;</span><br><span class="line">                res = res + compare[j];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (res == <span class="string">""</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">        <span class="comment">//提前退出的条件，剪枝j</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="15、三数之和"><a href="#15、三数之和" class="headerlink" title="15、三数之和"></a>15、三数之和</h2><p>给你一个包含 n 个整数的数组 nums，判断 nums 中是否存在三个元素 a，b，c ，使得 a + b + c = 0 ？请你找出所有满足条件且不重复的三元组。注意：答案中不可以包含重复的三元组。</p>
<p>思路：关键在于如何保证不重复，用常规思路的话，先用三层for循环，之后再用哈希表去重；换一个思路，我们保持三重循环的大框架不变，只需要保证：第二重循环枚举到的元素不小于当前第一重循环枚举到的元素；第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。即这样就只有一种顺序被枚举了，可以先排序，然后再查找。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> threeSum = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>&#123;</span><br><span class="line">    nums.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (a - b);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">let</span> res = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;<span class="comment">//在算法范畴上，进行逻辑的剪枝，此时后续不可能成立</span></span><br><span class="line">        <span class="keyword">let</span> target = -nums[i];</span><br><span class="line">        <span class="keyword">if</span> ((target + nums[i - <span class="number">1</span>] == <span class="number">0</span>) &amp;&amp; i &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">            <span class="comment">//从左往右遍历，此时遇到重复的则直接跳过，使用continue到for的下一个</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//找到一个后，后续的使用双指针来遍历查找，同时去重</span></span><br><span class="line">        <span class="keyword">let</span> left = i + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">let</span> right = nums.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (left &lt; right) &#123;</span><br><span class="line">            <span class="keyword">let</span> n2 = nums[left];</span><br><span class="line">            <span class="keyword">let</span> n3 = nums[right];</span><br><span class="line">            <span class="keyword">if</span> (n2 + n3 === target) &#123;</span><br><span class="line">                res.push([nums[i], n2, n3]);</span><br><span class="line">                <span class="keyword">while</span> (left &lt; right &amp;&amp; nums[left] === n2) left++;</span><br><span class="line">                <span class="comment">//去重复，后面有相同的则这里直接left++跳过去</span></span><br><span class="line">                <span class="keyword">while</span> (left &lt; right &amp;&amp; nums[right] === n3) right++;</span><br><span class="line">                <span class="comment">//这里在去重时，不要忘记基本的left &lt; right</span></span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span>(n2 + n3 &lt; target) &#123;</span><br><span class="line">                left++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                right--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="16、最接近的三数之和"><a href="#16、最接近的三数之和" class="headerlink" title="16、最接近的三数之和"></a>16、最接近的三数之和</h2><p>题目：给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数，使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。</p>
<p>思路：同样的思路，排序加双指针的做法能够很好地判断该如何去判断怎么去移动；同样，本题中不需要去考虑重复的问题了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> threeSumClosest = <span class="function"><span class="keyword">function</span>(<span class="params">nums, target</span>) </span>&#123;</span><br><span class="line">    nums.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (a - b);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">let</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> compare = <span class="literal">Infinity</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> left = i + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">let</span> right = nums.length - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span> (left &lt; right) &#123;</span><br><span class="line">            <span class="keyword">let</span> compare2 = target - nums[left] - nums[right] - nums[i];</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">Math</span>.abs(compare2) &lt; compare) &#123;</span><br><span class="line">                ans = nums[i] + nums[right] + nums[left];</span><br><span class="line">                compare = <span class="built_in">Math</span>.abs(compare2)</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (compare2 &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                right--;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                left++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="17、电话号码的组合"><a href="#17、电话号码的组合" class="headerlink" title="17、电话号码的组合"></a>17、电话号码的组合</h2><p>给定一个仅包含数字 <code>2-9</code> 的字符串，返回所有它能表示的字母组合。给出数字到字母的映射如下（与电话按键相同）。注意 1 不对应任何字母。</p>
<p>思路：看上去十分容易，本质是一个三叉树的遍历方法，可以用BFS或者DFS。由这个题，我们可以先引申一下深度遍历和广度遍历的思路逻辑与基本方法。</p>
<p>1、DFS回溯（回溯是一种算法思想，一般可以用递归来实现。通俗点讲回溯就是一种试探,类似于穷举,但回溯有“剪枝”功能,）</p>
<p>回溯本质是暴力搜索，在问题的解空间树中，用 DFS 的方式，从根节点出发搜索整个解空间。如果要找出所有的解，则要搜索整个子树，如果只用找出一个解，则搜到一个解就可以结束搜索。类似「找出所有可能的组合」的问题，适合回溯算法。</p>
<p>回溯类题目，有三个关键点：</p>
<p>(1).选择:决定了你每个节点有哪些分支，可以帮助你构建出解的空间树。</p>
<p>(2).约束条件:用来剪枝，剪去不满足约束条件的子树，避免无效的搜索。</p>
<p>(3).目标:决定了何时捕获解，或者剪去得不到解的子树，提前回溯。</p>
<p>回溯法实质:它的求解过程实质上是先序遍历一棵“状态树”的过程。只不过，这棵树不是遍历前预先建立的，而是隐含在遍历过程中。如果认识到这点，很多问题的递归过程设计也就迎刃而解了。【回溯与递归的区别】回溯这个算法思想可以由递归这个算法结构来实现</p>
<p>我们构建一个递归来实现DFS，。递归的关键：递归关系与递归终止条件。（其他的扔给递归）</p>
<p>1、找整个递归的终止条件：递归应该在什么时候结束？2、找返回值：应该给下一级返回什么信息？3、本级递归应该做什么：在这一级递归中，应该完成什么任务？</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> letterCombinations = <span class="function">(<span class="params">digits</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (digits.length === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> [];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> res = [];</span><br><span class="line">    <span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>([[<span class="string">'2'</span>,<span class="string">'abc'</span>], [<span class="string">'3'</span>,<span class="string">'def'</span>],[<span class="string">'4'</span>,<span class="string">'ghi'</span>],[<span class="string">'5'</span>,<span class="string">'jkl'</span>],[<span class="string">'6'</span>,<span class="string">'mno'</span>],[<span class="string">'7'</span>,<span class="string">'pqrs'</span>],[<span class="string">'8'</span>,<span class="string">'tuv'</span>],[<span class="string">'9'</span>,<span class="string">'wxyz'</span>]]);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> dfs = <span class="function">(<span class="params">curStr, i</span>) =&gt;</span> &#123;<span class="comment">//递归的传递参数包括，当前已经遍历树的结果、以及层数i</span></span><br><span class="line">        <span class="keyword">if</span> (i &gt; digits.length - <span class="number">1</span>) &#123;</span><br><span class="line">            res.push(curStr);</span><br><span class="line">            <span class="keyword">return</span>;<span class="comment">//一个树的递归分支结束</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">let</span> letters = map.get(digits[i]);</span><br><span class="line">        <span class="keyword">for</span> (j <span class="keyword">of</span> letters) &#123;</span><br><span class="line">            <span class="comment">//for in 用于遍历对象,for of用于遍历有Iterator接口的对象,</span></span><br><span class="line">            dfs(curStr, i+<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    dfs(<span class="string">""</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2、BFS广度搜索的方法</p>
<p>BFS通常是维护一个队列，即一层层地进行遍历，每次将对应层数的叶子加到之前层上，这里可以使用队列先进先出来解决，先进先出，依次地每次更新对应的下一层的结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">digits</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;string[]&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> letterCombinations = <span class="function">(<span class="params">digits</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (digits.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> [];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> res = [];</span><br><span class="line">    <span class="keyword">let</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>([[<span class="string">'2'</span>,<span class="string">'abc'</span>], [<span class="string">'3'</span>,<span class="string">'def'</span>],[<span class="string">'4'</span>,<span class="string">'ghi'</span>],[<span class="string">'5'</span>,<span class="string">'jkl'</span>],[<span class="string">'6'</span>,<span class="string">'mno'</span>],[<span class="string">'7'</span>,<span class="string">'pqrs'</span>],[<span class="string">'8'</span>,<span class="string">'tuv'</span>],[<span class="string">'9'</span>,<span class="string">'wxyz'</span>]]);</span><br><span class="line">    <span class="keyword">let</span> queue = [];</span><br><span class="line">    queue.push(<span class="string">''</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; digits.length; i++) &#123;<span class="comment">//bfs二叉树的层数，就是digits的长度</span></span><br><span class="line">        <span class="keyword">let</span> levelSize = queue.length;<span class="comment">//获取当前层的节点数，从而能逐个让当前层节点出列，更新接上对应后续节点后再依次入列</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; levelSize; j++) &#123;</span><br><span class="line">            <span class="keyword">let</span> curStr = queue.shift(); <span class="comment">//模拟队列</span></span><br><span class="line">            <span class="keyword">let</span> letters = map.get(digits[i]);</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">let</span> k <span class="keyword">of</span> letters) &#123;</span><br><span class="line">                queue.push(curStr + k);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> queue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="18、四数之和"><a href="#18、四数之和" class="headerlink" title="18、四数之和"></a>18、四数之和</h2><p>题目：给定一个包含 n 个整数的数组 nums 和一个目标值 target，判断 nums 中是否存在四个元素 a，b，c 和 d ，使得 a + b + c + d 的值与 target 相等？找出所有满足条件且不重复的四元组</p>
<p>思路：其实跟前面的三数之和思路一样，后面两个数可以用双指针法来进行枚举；而前面两个数只能通过两层的for循环来实现。同样，先对数组进行排序，且添加每个数时都要进行重复判断。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fourSum = <span class="function"><span class="keyword">function</span>(<span class="params">nums, target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> quadruplets = [];</span><br><span class="line">    <span class="keyword">if</span> (nums.length &lt; <span class="number">4</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> quadruplets;</span><br><span class="line">    &#125;</span><br><span class="line">    nums.sort(<span class="function">(<span class="params">x, y</span>) =&gt;</span> x - y);</span><br><span class="line">    <span class="keyword">const</span> length = nums.length;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; length - <span class="number">3</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i &gt; <span class="number">0</span> &amp;&amp; nums[i] === nums[i - <span class="number">1</span>]) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;<span class="comment">//从左往右遍历，有跟上一条重复的则跳过</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] + nums[i + <span class="number">1</span>] + nums[i + <span class="number">2</span>] + nums[i + <span class="number">3</span>] &gt; target) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] + nums[length - <span class="number">3</span>] + nums[length - <span class="number">2</span>] + nums[length - <span class="number">1</span>] &lt; target) &#123;</span><br><span class="line">            <span class="keyword">continue</span>;<span class="comment">//上面两种情况都是剪枝，以减少事件复杂度</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = i + <span class="number">1</span>; j &lt; length - <span class="number">2</span>; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (j &gt; i + <span class="number">1</span> &amp;&amp; nums[j] === nums[j - <span class="number">1</span>]) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] + nums[j] + nums[j + <span class="number">1</span>] + nums[j + <span class="number">2</span>] &gt; target) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] + nums[j] + nums[length - <span class="number">2</span>] + nums[length - <span class="number">1</span>] &lt; target) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">let</span> left = j + <span class="number">1</span>, right = length - <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">while</span> (left &lt; right) &#123;</span><br><span class="line">                <span class="keyword">const</span> sum = nums[i] + nums[j] + nums[left] + nums[right];</span><br><span class="line">                <span class="keyword">if</span> (sum === target) &#123;</span><br><span class="line">                    quadruplets.push([nums[i], nums[j], nums[left], nums[right]]);</span><br><span class="line">                    <span class="keyword">while</span> (left &lt; right &amp;&amp; nums[left] === nums[left + <span class="number">1</span>]) &#123;</span><br><span class="line">                        left++;</span><br><span class="line">                    &#125;</span><br><span class="line">                    left++;</span><br><span class="line">                    <span class="keyword">while</span> (left &lt; right &amp;&amp; nums[right] === nums[right - <span class="number">1</span>]) &#123;</span><br><span class="line">                        right--;</span><br><span class="line">                    &#125;</span><br><span class="line">                    right--;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (sum &lt; target) &#123;</span><br><span class="line">                    left++;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    right--;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> quadruplets;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="19、删除链表的倒数第N个结点"><a href="#19、删除链表的倒数第N个结点" class="headerlink" title="19、删除链表的倒数第N个结点"></a>19、删除链表的倒数第N个结点</h2><p>题目:给你一个链表，删除链表的倒数第 <code>n</code> 个结点，并且返回链表的头结点。进阶：你能尝试使用一趟扫描实现吗？</p>
<p>思路：不要求时间复杂度的话，先扫一遍来确定链表的长度，而后再根据长度找到倒数第N个结点；要使用一趟扫描实现的话，可以使用双指针来进行。第一个指针比第二个快n个，这样第二个指向尾节点时，则第一个指向要删除的结点。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> removeNthFromEnd = <span class="function"><span class="keyword">function</span>(<span class="params">head, n</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> start = <span class="keyword">new</span> ListNode;</span><br><span class="line">    <span class="comment">//一般先定义一个空结点放到现有的头节点前面，之后以该空结点作链表处理，从而能够避免因链表长度不够导致的报错</span></span><br><span class="line">    start.next = head;</span><br><span class="line">    <span class="keyword">let</span> left = start;</span><br><span class="line">    <span class="keyword">let</span> right = start;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        right = right.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (right.next != <span class="literal">null</span>) &#123;</span><br><span class="line">        left = left.next;</span><br><span class="line">        right = right.next;</span><br><span class="line">    &#125;</span><br><span class="line">    left.next = left.next.next;</span><br><span class="line">    <span class="keyword">return</span> start.next;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="20、有效的括号"><a href="#20、有效的括号" class="headerlink" title="20、有效的括号"></a>20、有效的括号</h2><p>题目：给定一个只包括 ‘(‘，’)’，’{‘，’}’，’[‘，’]’ 的字符串，判断字符串是否有效。有效字符串需满足：左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注意空字符串可被认为是有效字符串。</p>
<p>思路：类似于栈的操作，判断很简单，这里就不赘述了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">s</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;boolean&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;string&#125;</span> <span class="variable">s</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;boolean&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> isValid = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (s.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> compare = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; s.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (s[i] === <span class="string">"("</span> || s[i] === <span class="string">"["</span> || s[i] === <span class="string">"&#123;"</span>) &#123;</span><br><span class="line">            compare.push(s[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (s[i] === <span class="string">")"</span>) &#123;</span><br><span class="line">            <span class="keyword">let</span> a = compare.pop();</span><br><span class="line">            <span class="keyword">if</span> (a !== <span class="string">"("</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (s[i] === <span class="string">"]"</span>) &#123;</span><br><span class="line">            <span class="keyword">let</span> a = compare.pop();</span><br><span class="line">            <span class="keyword">if</span> (a !== <span class="string">"["</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (s[i] === <span class="string">"&#125;"</span>) &#123;</span><br><span class="line">            <span class="keyword">let</span> a = compare.pop();</span><br><span class="line">            <span class="keyword">if</span> (a !== <span class="string">"&#123;"</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (compare.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/24/node/egg-node%E5%AE%9E%E6%88%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/24/node/egg-node%E5%AE%9E%E6%88%98/" class="post-title-link" itemprop="url">数据库</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-08-24 12:18:39" itemprop="dateCreated datePublished" datetime="2021-08-24T12:18:39+08:00">2021-08-24</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:19:25" itemprop="dateModified" datetime="2021-11-03T16:19:25+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/node/" itemprop="url" rel="index"><span itemprop="name">node</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>在WEB应用中最常见的就是I/O操作，而只要涉及业务处理及数据持久化，就需要使用数据库。</p>
<p>关系型数据库：基础是关系代数，主要处理结构化数据，表连接在一起时会产生一个笛卡尔乘积。关系型数据库主要采用SQL（结构化查询语言）。例如MySQL。</p>
<p>非关系型数据库：主要用于解决四个问题：1、数据的高并发读/写；2、数据的高可用；3、海量数据的存储；4、实时分析。</p>
<p>所以非关系型数据库采用键值对形式存储，缓存的好处是极其明显的。</p>
<p>MongoDB是一个解与关系型数据建库和非关系型数据库之间的产品，是NoSQL中功能最丰富、最像关系型数据库的。MongoDB最大的特点是它的查询语言功能非常强大，其语法有点类似于面向对象的查询语言。</p>
<h1 id="MongoDB"><a href="#MongoDB" class="headerlink" title="MongoDB"></a>MongoDB</h1>
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/15/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/jest%E6%B5%8B%E8%AF%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/15/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/jest%E6%B5%8B%E8%AF%95/" class="post-title-link" itemprop="url">Jest测试（待整理）</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-08-15 21:28:16" itemprop="dateCreated datePublished" datetime="2021-08-15T21:28:16+08:00">2021-08-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:32:46" itemprop="dateModified" datetime="2021-11-03T16:32:46+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/" itemprop="url" rel="index"><span itemprop="name">学校项目应用</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h2 id="项目背景"><a href="#项目背景" class="headerlink" title="项目背景"></a>项目背景</h2><p>存量接口配置旧代码使用Angular框架实现，后续需使用React框架对代码进行重构，为确保重构后代码能够实现原代码功能，而并不造成意外的bug，需对重构后代码书写测试代码以确保其功能的实现。</p>
<h2 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h2><p>前端测试主要分为 3 种：单元测试（Unit Test）、集成测试（Integration Test）、UI 测试（UI Test）。</p>
<p>单元测试是最容易实现的：代码中多个组件共用的工具类库、多个组件共用的子组件等。通常情况下，在公共函数/组件中一定要有单元测试来保证代码能够正常工作。单元测试也应该是项目中数量最多、覆盖率最高的。</p>
<p>集成测试通常被应用在：耦合度较高的函数/组件、经过二次封装的函数/组件、多个函数/组件组合而成的函数/组件等。</p>
<p>UI测试更贴近于我们的开发流程。在前后端分离的开发模式中，前端开发通常会使用到 Mock 的服务器和数据。因而我们需要在开发基本完成后进行相应的 UI 测试。UI 测试的自动化程度还不高，大多数还依赖于手工测试。在一些自动化测试工具中有创建快照的功能，也能帮助我们在一定程度上实现 UI 测试的自动化。</p>
<p>针对重构后部分React代码的功能测试，主要是为了确保其单元组件功能性以及数据传输与显示逻辑的正确，因此更重要的在于其各个组件的单元测试以及各组件本身的UI测试，首要任务是对重构后React各模块的单元测试任务，同时需进行单体的UI测试。</p>
<p>由于项目并未整个重构完成，使用整体集成测试过于复杂且无必要，未重构代码在之前使用中经过多次测试，可以看作稳定的整体，因此其数据传输的格式与方法固定，在理清传输方式后，在外层模拟未重构代码向当前已重构模块进行的数据传输方式，便能够构建出重构模块应有的上下文环境，便能对重构模块整体进行集成测试。</p>
<h2 id="实践步骤"><a href="#实践步骤" class="headerlink" title="实践步骤"></a>实践步骤</h2><h3 id="1、-测试框架选用与依赖配置"><a href="#1、-测试框架选用与依赖配置" class="headerlink" title="1、 测试框架选用与依赖配置"></a>1、 测试框架选用与依赖配置</h3><p>​    Jest是Facebook开源的一个单元测试框架，也是现在应用最广泛的。由于项目使用React hooks实现，因此常用于类组件的UI测试ReactTestUtils不再适用，我们用React hooks testing library来代替，这是一个专门用于测试React hook的库。</p>
<p>项目使用TypeScript编写的TSX代码来实现，在打包运行时会有webpack进行代码的转译。因此测试过程中同样需要添加各类代码转译模块。</p>
<p>1、在jest运行时的全局变量中注入ts-jest，调用与webpack一样的ts.config配置文件，实现s对tsx文件的转译。后续在配置中，使用正则匹配tsx文件，使其调用ts-jest即可。</p>
<p>2、在用ts-jest转译时，由于某些原因，可能无法识别import，这时需要额外配置babel-jest完成从ES6+到ES5的转译。</p>
<p>3、对于.scss、.css 格式文件，配置时使用 jest-css-modules处理，处理方式同上</p>
<h3 id="2、-Redux与React-Intl模拟"><a href="#2、-Redux与React-Intl模拟" class="headerlink" title="2、 Redux与React-Intl模拟"></a>2、 Redux与React-Intl模拟</h3><p>1、该项目使用Redux实现组件间数据的传输与交互，因此在单元测试之前，我们需要先模拟一层Redux的Provider，包裹在组件的外层，来向被测组件传输数据。</p>
<p>2、项目使用React-Intl来进行一个。国际化语言的分类处理，因此我们同样需要在模拟一层IntlProvider，包裹在组件外层</p>
<p>引入Redux和React-Intl后，利用两层Provider完善上下文环境的配置，这时候我们就可以开始进行单元测试的编写了。</p>
<p> 项目采用React-hooks来编写，虽然hook是一个函数，但实际运行会涉及到很多React运行时的东西，因此我们不能用测试普通函数的方法来测试它们，需利用React hooks testing library库来进行测试。</p>
<h3 id="3、-单元测试与UI快照生成"><a href="#3、-单元测试与UI快照生成" class="headerlink" title="3、 单元测试与UI快照生成"></a>3、 单元测试与UI快照生成</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//jest配置</span></span><br><span class="line">  <span class="keyword">let</span> ans = &#123;</span><br><span class="line">    <span class="comment">// 测试用例运行在一个类似于浏览器的环境里，可以调用浏览器的 API</span></span><br><span class="line">    testEnvironment: <span class="string">'jest-environment-jsdom-fourteen'</span>,</span><br><span class="line">    <span class="comment">// 以 &lt;rootDir&gt;/src 这个目录做为根目录来搜索测试文件（模块）</span></span><br><span class="line">    roots: [</span><br><span class="line">        <span class="string">'&lt;rootDir&gt;/src'</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// 在测试环境准备好之后且每个测试文件执行之前运行下述文件</span></span><br><span class="line">    setupFilesAfterEnv: [</span><br><span class="line">        <span class="string">'&lt;rootDir&gt;/test/index.ts'</span>,</span><br><span class="line">    ],</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试文件模块之间的引用应该是自己实现了一套类似于 Node 的引用机制</span></span><br><span class="line">    <span class="comment">// 不过自己可以配置，下面 module 开头的都是配置这个的，都用例子来说明</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 例如，require('./a') 语句会先找 `a.ts`，找不到找 `a.js`</span></span><br><span class="line">    moduleFileExtensions: [</span><br><span class="line">        <span class="string">'ts'</span>,</span><br><span class="line">        <span class="string">'js'</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// 例如，require('a') 语句会递归往上层的 node_modules 中寻找 a 模块</span></span><br><span class="line">    moduleDirectories: [</span><br><span class="line">        <span class="string">'node_modules'</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// 例如，require('@/a.js') 会解析成 require('&lt;rootDir&gt;/src/a.js')</span></span><br><span class="line">    moduleNameMapper: &#123;</span><br><span class="line">        <span class="string">'^@/test$'</span>: <span class="string">'&lt;rootDir&gt;/test/index.js'</span>,</span><br><span class="line">        <span class="string">'^@/test/(.*)$'</span>: <span class="string">'&lt;rootDir&gt;/test/$1'</span>,</span><br><span class="line">        <span class="string">'^@/(.*)$'</span>: <span class="string">'&lt;rootDir&gt;/src/$1'</span>,</span><br><span class="line">        <span class="string">'\\.(css|sass|scss)$'</span>: <span class="string">'identity-obj-proxy'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 转译下列模块为 Jest 能识别的代码</span></span><br><span class="line">    transform: &#123;</span><br><span class="line">        <span class="string">'\\.(styl)$'</span>: <span class="string">'jest-css-modules'</span>,</span><br><span class="line">        <span class="string">'\\.(sass|scss)$'</span>: <span class="string">'jest-css-modules'</span>,</span><br><span class="line">        <span class="string">'.*\\.(j|t)s$'</span>: <span class="string">'ts-jest'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// 收集这些文件的测试覆盖率</span></span><br><span class="line">    collectCoverageFrom: [</span><br><span class="line">        <span class="string">'src/**/*.&#123;js,ts,tsx&#125;'</span>,</span><br><span class="line">        <span class="string">'!**/*.d.ts'</span>,</span><br><span class="line">    ],</span><br><span class="line">    transformIgnorePatterns: [</span><br><span class="line">        <span class="string">'node_modules/(?!vue-router)'</span>,</span><br><span class="line">    ],</span><br><span class="line">    snapshotSerializers: [</span><br><span class="line">        <span class="string">'jest-serializer-html'</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// 从下列文件中寻找测试文件</span></span><br><span class="line">    testMatch: [</span><br><span class="line">        <span class="comment">// Default</span></span><br><span class="line">        <span class="string">'**/test/**/*.js'</span>,</span><br><span class="line">        <span class="string">'**/__tests__/**/*.spec.js'</span>,</span><br><span class="line">        <span class="string">'**/__tests__/**/*.spec.ts'</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="comment">// 将 `ts-jest` 的配置注入到运行时的全局变量中</span></span><br><span class="line">    globals: &#123;</span><br><span class="line">        <span class="string">'ts-jest'</span>: &#123;</span><br><span class="line">            <span class="comment">// 是否使用 babel 配置来转译</span></span><br><span class="line">            babelConfig: <span class="literal">true</span>,</span><br><span class="line">            <span class="comment">// 编译 Typescript 所依赖的配置</span></span><br><span class="line">            tsConfig: <span class="string">'&lt;rootDir&gt;/tsconfig.test.json'</span>,</span><br><span class="line">            <span class="comment">// 是否启用报告诊断，这里是不启用</span></span><br><span class="line">            diagnostics: <span class="literal">false</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">// Jest 文档中无此配置，应该已经过时了</span></span><br><span class="line">    name: <span class="string">'Vuetify'</span>,</span><br><span class="line">    <span class="comment">// 测试文件名旁边显示的标识</span></span><br><span class="line">    displayName: <span class="string">'Vuetify'</span>,</span><br><span class="line">    <span class="comment">// 在测试环境准备好之前且每个测试文件执行之前运行下述模块</span></span><br><span class="line">    setupFiles: [</span><br><span class="line">        <span class="string">'jest-canvas-mock'</span></span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*测试DOM元素，UI测试大部分由底层的EViewUI库来实现，</span></span><br><span class="line"><span class="comment">剩下少部分UI测试使用DOM元素测试即可。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; render, cleanup &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span>;</span><br><span class="line"><span class="keyword">import</span> TestElements <span class="keyword">from</span> <span class="string">'../components/TestElements'</span></span><br><span class="line"></span><br><span class="line">afterEach(cleanup);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should equal to 0'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestElements</span> /&gt;</span></span>); </span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="number">0</span>)</span><br><span class="line">   &#125;);</span><br><span class="line"></span><br><span class="line">   it(<span class="string">'should be enabled'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestElements</span> /&gt;</span></span>);</span><br><span class="line">    expect(getByTestId(<span class="string">'button-up'</span>)).not.toHaveAttribute(<span class="string">'disabled'</span>)</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should be disabled'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestElements</span> /&gt;</span></span>); </span><br><span class="line">    expect(getByTestId(<span class="string">'button-down'</span>)).toBeDisabled()</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 测试事件响应，使用fireEvent触发事件，再对之后产生的结果进行断言，</span></span><br><span class="line"><span class="comment">判断事件响应后的逻辑是否正确。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; render, cleanup, fireEvent &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span>;</span><br><span class="line"><span class="keyword">import</span> TestEvents <span class="keyword">from</span> <span class="string">'../components/TestEvents'</span></span><br><span class="line"></span><br><span class="line">  afterEach(cleanup);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'increments counter'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestEvents</span> /&gt;</span></span>); </span><br><span class="line"></span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'button-up'</span>))</span><br><span class="line"></span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'1'</span>)</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'decrements counter'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestEvents</span> /&gt;</span></span>); </span><br><span class="line"></span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'button-down'</span>))</span><br><span class="line"></span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'-1'</span>)</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*异步事件的处理，对于异步事件，使用await进行处理操作 */</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; render, cleanup, fireEvent, waitForElement &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span>;</span><br><span class="line"><span class="keyword">import</span> TestAsync <span class="keyword">from</span> <span class="string">'../components/TestAsync'</span></span><br><span class="line"></span><br><span class="line">afterEach(cleanup);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'increments counter after 0.5s'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId, getByText &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestAsync</span> /&gt;</span></span>); </span><br><span class="line"></span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'button-up'</span>))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> counter = <span class="keyword">await</span> waitForElement(<span class="function"><span class="params">()</span> =&gt;</span> getByText(<span class="string">'1'</span>)) </span><br><span class="line"></span><br><span class="line">    expect(counter).toHaveTextContent(<span class="string">'1'</span>)</span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*后续的Redux、React Context、React Router、HTTP请求的测试由于时间原因还未进行实践</span></span><br><span class="line"><span class="comment">但基本的测试逻辑已经有了参考。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; createStore &#125; <span class="keyword">from</span> <span class="string">'redux'</span></span><br><span class="line"><span class="keyword">import</span> &#123; Provider &#125; <span class="keyword">from</span> <span class="string">'react-redux'</span></span><br><span class="line"><span class="keyword">import</span> &#123; render, cleanup, fireEvent &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; initialState, reducer &#125; <span class="keyword">from</span> <span class="string">'../store/reducer'</span></span><br><span class="line"><span class="keyword">import</span> TestRedux <span class="keyword">from</span> <span class="string">'../components/TestRedux'</span></span><br><span class="line"><span class="comment">/*后续的Redux、React Context、React Router、HTTP请求的测试由于时间原因还未进行实践</span></span><br><span class="line"><span class="comment">但基本的测试逻辑已经有了参考。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">//Redux测试参考</span></span><br><span class="line"><span class="keyword">const</span> renderWithRedux = (</span><br><span class="line">  component,</span><br><span class="line">  &#123; initialState, store = createStore(reducer, initialState) &#125; = &#123;&#125;</span><br><span class="line">) =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    ...render(<span class="xml"><span class="tag">&lt;<span class="name">Provider</span> <span class="attr">store</span>=<span class="string">&#123;store&#125;</span>&gt;</span>&#123;component&#125;<span class="tag">&lt;/<span class="name">Provider</span>&gt;</span></span>),</span><br><span class="line">    store,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"> afterEach(cleanup);</span><br><span class="line"></span><br><span class="line">it(<span class="string">'checks initial state is equal to 0'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = renderWithRedux(<span class="xml"><span class="tag">&lt;<span class="name">TestRedux</span> /&gt;</span></span>)</span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'0'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'increments the counter through redux'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = renderWithRedux(<span class="xml"><span class="tag">&lt;<span class="name">TestRedux</span> /&gt;</span></span>, </span><br><span class="line">      &#123;<span class="attr">initialState</span>: &#123;<span class="attr">count</span>: <span class="number">5</span>&#125;</span><br><span class="line">  &#125;)</span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'button-up'</span>))</span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'6'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'decrements the counter through redux'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId&#125; = renderWithRedux(<span class="xml"><span class="tag">&lt;<span class="name">TestRedux</span> /&gt;</span></span>, &#123;</span><br><span class="line">      initialState: &#123; <span class="attr">count</span>: <span class="number">100</span> &#125;,</span><br><span class="line">    &#125;)</span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'button-down'</span>))</span><br><span class="line">    expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'99'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"><span class="comment">//React Context测试参考</span></span><br><span class="line">  <span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line">  <span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line">  <span class="keyword">import</span> &#123; render, cleanup,  fireEvent &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span></span><br><span class="line">  <span class="keyword">import</span> CounterProvider, &#123; CounterContext, Counter &#125; <span class="keyword">from</span> <span class="string">'../components/TestContext'</span></span><br><span class="line">  <span class="keyword">const</span> renderWithContext = (</span><br><span class="line">    component) =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      ...render(</span><br><span class="line">          &lt;CounterProvider value=&#123;CounterContext&#125;&gt;</span><br><span class="line">              &#123;component&#125;</span><br><span class="line">          &lt;<span class="regexp">/CounterProvider&gt;)</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">  afterEach(cleanup);</span></span><br><span class="line"><span class="regexp">  it('checks if initial state is equal to 0', () =&gt; &#123;</span></span><br><span class="line"><span class="regexp">      const &#123; getByTestId &#125; = renderWithContext(&lt;Counter /</span>&gt;)</span><br><span class="line">      expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'0'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">  </span><br><span class="line">  it(<span class="string">'increments the counter'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; getByTestId &#125; = renderWithContext(<span class="xml"><span class="tag">&lt;<span class="name">Counter</span> /&gt;</span></span>)</span><br><span class="line">  </span><br><span class="line">      fireEvent.click(getByTestId(<span class="string">'button-up'</span>))</span><br><span class="line">      expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'1'</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  </span><br><span class="line">    it(<span class="string">'decrements the counter'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="keyword">const</span> &#123; getByTestId&#125; = renderWithContext(<span class="xml"><span class="tag">&lt;<span class="name">Counter</span> /&gt;</span></span>)</span><br><span class="line">  </span><br><span class="line">      fireEvent.click(getByTestId(<span class="string">'button-down'</span>))</span><br><span class="line">      expect(getByTestId(<span class="string">'counter'</span>)).toHaveTextContent(<span class="string">'-1'</span>)</span><br><span class="line">    &#125;);</span><br><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; Router &#125; <span class="keyword">from</span> <span class="string">'react-router-dom'</span></span><br><span class="line"><span class="keyword">import</span> &#123; render, fireEvent &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span></span><br><span class="line"><span class="keyword">import</span> &#123; createMemoryHistory &#125; <span class="keyword">from</span> <span class="string">'history'</span></span><br><span class="line"><span class="keyword">import</span> TestRouter <span class="keyword">from</span> <span class="string">'../components/TestRouter'</span></span><br><span class="line"><span class="comment">//ReactRouter测试参考</span></span><br><span class="line"><span class="keyword">const</span> renderWithRouter = <span class="function">(<span class="params">component</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> history = createMemoryHistory()</span><br><span class="line">    <span class="keyword">return</span> &#123; </span><br><span class="line">    ...render (</span><br><span class="line">    &lt;Router history=&#123;history&#125;&gt;</span><br><span class="line">        &#123;component&#125;</span><br><span class="line">    &lt;<span class="regexp">/Router&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">it('should render the home page', () =&gt; &#123;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">  const &#123; container, getByTestId &#125; = renderWithRouter(&lt;TestRouter /</span>&gt;) </span><br><span class="line">  <span class="keyword">const</span> navbar = getByTestId(<span class="string">'navbar'</span>)</span><br><span class="line">  <span class="keyword">const</span> link = getByTestId(<span class="string">'home-link'</span>)</span><br><span class="line"></span><br><span class="line">  expect(container.innerHTML).toMatch(<span class="string">'Home page'</span>)</span><br><span class="line">  expect(navbar).toContainElement(link)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">it(<span class="string">'should navigate to the about page'</span>, ()=&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; container, getByTestId &#125; = renderWithRouter(<span class="xml"><span class="tag">&lt;<span class="name">TestRouter</span> /&gt;</span></span>) </span><br><span class="line"></span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'about-link'</span>))</span><br><span class="line"></span><br><span class="line">    expect(container.innerHTML).toMatch(<span class="string">'About page'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should navigate to the contact page with the params'</span>, ()=&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123; container, getByTestId &#125; = renderWithRouter(<span class="xml"><span class="tag">&lt;<span class="name">TestRouter</span> /&gt;</span></span>) </span><br><span class="line"></span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'contact-link'</span>))</span><br><span class="line"></span><br><span class="line">    expect(container.innerHTML).toMatch(<span class="string">'John Doe'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line"><span class="comment">//HTTP请求测试参考</span></span><br><span class="line">  <span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line">  <span class="keyword">import</span> <span class="string">"@testing-library/jest-dom/extend-expect"</span>;</span><br><span class="line">  <span class="keyword">import</span> &#123; render, waitForElement, fireEvent &#125; <span class="keyword">from</span> <span class="string">'@testing-library/react'</span></span><br><span class="line">  <span class="keyword">import</span> axiosMock <span class="keyword">from</span> <span class="string">'axios'</span></span><br><span class="line">  <span class="keyword">import</span> TestAxios <span class="keyword">from</span> <span class="string">'../components/TestAxios'</span></span><br><span class="line"></span><br><span class="line">  jest.mock(<span class="string">'axios'</span>)</span><br><span class="line">  </span><br><span class="line">  it(<span class="string">'should display a loading text'</span>, () =&gt; &#123;</span><br><span class="line">  </span><br><span class="line">   <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestAxios</span> /&gt;</span></span>)</span><br><span class="line">  </span><br><span class="line">    expect(getByTestId(<span class="string">'loading'</span>)).toHaveTextContent(<span class="string">'Loading...'</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">  </span><br><span class="line">  it(<span class="string">'should load and display the data'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> url = <span class="string">'/greeting'</span></span><br><span class="line">    <span class="keyword">const</span> &#123; getByTestId &#125; = render(<span class="xml"><span class="tag">&lt;<span class="name">TestAxios</span> <span class="attr">url</span>=<span class="string">&#123;url&#125;</span> /&gt;</span></span>)</span><br><span class="line">  </span><br><span class="line">    axiosMock.get.mockResolvedValueOnce(&#123;</span><br><span class="line">      data: &#123; <span class="attr">greeting</span>: <span class="string">'hello there'</span> &#125;,</span><br><span class="line">    &#125;)</span><br><span class="line">  </span><br><span class="line">    fireEvent.click(getByTestId(<span class="string">'fetch-data'</span>))</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">const</span> greetingData = <span class="keyword">await</span> waitForElement(<span class="function"><span class="params">()</span> =&gt;</span> getByTestId(<span class="string">'show-data'</span>))</span><br><span class="line">  </span><br><span class="line">    expect(axiosMock.get).toHaveBeenCalledTimes(<span class="number">1</span>)</span><br><span class="line">    expect(axiosMock.get).toHaveBeenCalledWith(url)</span><br><span class="line">    expect(greetingData).toHaveTextContent(<span class="string">'hello there'</span>)</span><br><span class="line">  &#125;)</span><br></pre></td></tr></table></figure>



<h3 id="4、重构整体集成测试"><a href="#4、重构整体集成测试" class="headerlink" title="4、重构整体集成测试"></a>4、重构整体集成测试</h3>
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/08/15/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/JS%E6%89%8B%E5%86%99%E9%97%AE%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/15/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/JS%E6%89%8B%E5%86%99%E9%97%AE%E9%A2%98/" class="post-title-link" itemprop="url">JS手写问题</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-08-15 21:28:16" itemprop="dateCreated datePublished" datetime="2021-08-15T21:28:16+08:00">2021-08-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:24:34" itemprop="dateModified" datetime="2021-11-03T16:24:34+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93/" itemprop="url" rel="index"><span itemprop="name">面试总结</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近在参加秋招，遇到了各式的手撕代码的面试题，有当场写出的，有以前写过现在死活不知道怎么办的，还有写出基本功能，但对于细节上的优化相当不足，不能体现自己编码的严谨性的，因此，在这里总结了下手写JS问题的博客。</p>
<h2 id="1、promise相关"><a href="#1、promise相关" class="headerlink" title="1、promise相关"></a>1、promise相关</h2><p><strong>promise相关的手写问题主要有两个吧，一个是自己实现一个简单的promise.all的方法，能够让传入的promise同时并行执行，直到所有的promise均执行成功后再执行回调函数。一个是promise的串行执行，让promise由并行转为依次串行执行</strong></p>
<p>知识点扩充：基本类型保存再栈中，值不可变、但可以重新赋值，是值之间的比较。引用类型保存再堆中，值可变，是引用的比较。这里往往会引出深浅拷贝的问题，因为不用拷贝来进行复制对象时，由于传递的是值的引用，因此指向的是同一对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">实现一个promiseAll([ PromiseA, PromiseB, ])</span></span><br><span class="line"><span class="comment">使PromiseX能并行执行，并返回Promise？</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> promiseAll = <span class="function"><span class="keyword">function</span> (<span class="params">arr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> (arr.length === <span class="number">0</span>) &#123;</span><br><span class="line">        resolve([]);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> result = []</span><br><span class="line">        <span class="keyword">let</span> num = <span class="number">0</span>; </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">          arr[i].then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            num++;</span><br><span class="line">            result[i] = res;</span><br><span class="line">            <span class="keyword">if</span> (num == arr.length) resolve(result);</span><br><span class="line">          &#125;, reject);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> pro1 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">res,rej</span>) =&gt;</span> &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      res(<span class="string">'1'</span>)</span><br><span class="line">    &#125;,<span class="number">1000</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">const</span> pro2 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">res,rej</span>) =&gt;</span> &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      res(<span class="string">'2'</span>)</span><br><span class="line">    &#125;,<span class="number">2000</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">const</span> pro3 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">res,rej</span>) =&gt;</span> &#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      res(<span class="string">'5'</span>)</span><br><span class="line">    &#125;,<span class="number">5000</span>)</span><br><span class="line">  &#125;)</span><br><span class="line">  </span><br><span class="line">  promiseAll([pro1, pro2, pro3]).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(res);</span><br><span class="line">  &#125;);</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  <span class="comment">/*</span></span><br><span class="line"><span class="comment">  实现Promise.run([returnPromiseA, returnPromiseB, returnPromiseC, ])，</span></span><br><span class="line"><span class="comment">  使得returnPromiseX方法返回的Promise能够串行执行，返回Promise？</span></span><br><span class="line"><span class="comment">  </span></span><br><span class="line"><span class="comment">  在promise中，如果then方法返回的是一个promise对象，</span></span><br><span class="line"><span class="comment">  那么执行下一个then的时候必定是在上一个人then执行完之后执行</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  <span class="keyword">const</span> promiseRun = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ans = [];</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">        arr.reduce(<span class="function">(<span class="params">pre, cur</span>) =&gt;</span> &#123;</span><br><span class="line">          <span class="keyword">return</span> pre.then(cur);</span><br><span class="line">        &#125;,<span class="built_in">Promise</span>.resolve());</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line">  </span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">returnPromiseA</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>)=&gt;</span>&#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123; resolve(<span class="string">"1"</span>) &#125;, <span class="number">1000</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">returnPromiseB</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>)=&gt;</span>&#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123; resolve(<span class="string">"2"</span>) &#125;, <span class="number">2000</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">returnPromiseC</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve</span>)=&gt;</span>&#123;</span><br><span class="line">      setTimeout(<span class="function"><span class="params">()</span>=&gt;</span>&#123; resolve(<span class="string">"5"</span>) &#125;, <span class="number">5000</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  promiseRun([returnPromiseA, returnPromiseB, returnPromiseC]).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(res);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure>



<h2 id="2、深拷贝、浅拷贝"><a href="#2、深拷贝、浅拷贝" class="headerlink" title="2、深拷贝、浅拷贝"></a>2、深拷贝、浅拷贝</h2><p><strong>深浅拷贝都是对引用类型的复制。浅拷贝只拷贝一层，对于深层次的对象级别，仍然会拷贝引用；而深拷贝能够拷贝多层次的嵌套对象。</strong></p>
<p>1、浅拷贝</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">simpleCopy</span> (<span class="params">obj1</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> obj2 = <span class="built_in">Array</span>.isArray(obj1) ? [] : &#123;&#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> obj1) &#123;</span><br><span class="line">        obj2[i] = obj1[i]</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> obj2</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2、初始版本</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> target === <span class="string">'object'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> cloneTarget = &#123;&#125;;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> target) &#123;</span><br><span class="line">            cloneTarget[key] = clone(target[key]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cloneTarget;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> target;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>3、考虑数组的情况</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> target === <span class="string">'object'</span>) &#123;</span><br><span class="line">        <span class="comment">//只需要在初始化暂存变量值时，用Array.isArray来判断为[]，还是&#123;&#125;即可。</span></span><br><span class="line">        <span class="keyword">let</span> cloneTarget = <span class="built_in">Array</span>.isArray(target) ? [] : &#123;&#125;;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> target) &#123;</span><br><span class="line">            cloneTarget[key] = clone(target[key]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cloneTarget;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> target;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>4、循环引用问题</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">上面的代码看似能实现功能，但是当对象存在循环引用问题时，则会导致递归出现死循环。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> target = &#123;</span><br><span class="line">    field1: <span class="number">1</span>,</span><br><span class="line">    field2: <span class="literal">undefined</span>,</span><br><span class="line">    field3: &#123;</span><br><span class="line">        child: <span class="string">'child'</span></span><br><span class="line">    &#125;,</span><br><span class="line">    field4: [<span class="number">2</span>, <span class="number">4</span>, <span class="number">8</span>]</span><br><span class="line">&#125;;</span><br><span class="line">target.target = target;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">解决循环引用问题，我们可以额外开辟一个存储空间，来存储当前对象和拷贝对象的对应关系，当需要拷贝当前对象时，先去存储空间中找，有没有拷贝过这个对象，如果有的话直接返回，如果没有的话继续拷贝，这样就巧妙化解的循环引用的问题。</span></span><br><span class="line"><span class="comment">这里我们使用Map这种数据结构</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">target, map = new Map(</span>)) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> target === <span class="string">'object'</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> cloneTarget =  <span class="built_in">Array</span>.isArray(target) ? [] : &#123;&#125;;</span><br><span class="line">        <span class="keyword">if</span> (map.has(target)) &#123;</span><br><span class="line">            <span class="keyword">return</span> map.get(target);</span><br><span class="line">        &#125;</span><br><span class="line">        map.set(target, cloneTarget);</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> target) &#123;</span><br><span class="line">            cloneTarget[key] = clone(target[key], map);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cloneTarget;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> target;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>5、其他数据类型处理</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在判断引用类型时，考虑function与null这种特殊情况</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isObject</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> type = <span class="keyword">typeof</span> target;</span><br><span class="line">    <span class="keyword">return</span> target !== <span class="literal">null</span> &amp;&amp; (type === <span class="string">'object'</span> || type === <span class="string">'function'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//对于引用类型，利用Object.toString()来获取准确引用类型</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getType</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.toString.call(target);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*对于之前说的object、array以及Map、set都属于可继续遍历的类型</span></span><br><span class="line"><span class="comment">我们通过拿到constructor的方式来通用地获取</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInit</span>(<span class="params">target</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> Ctor = target.constructor;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Ctor();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">target, map = new WeakMap(</span>)) </span>&#123;</span><br><span class="line">    <span class="comment">// 克隆原始类型</span></span><br><span class="line">    <span class="keyword">if</span> (!isObject(target)) &#123;</span><br><span class="line">        <span class="keyword">return</span> target;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    <span class="keyword">const</span> type = getType(target);</span><br><span class="line">    <span class="keyword">let</span> cloneTarget;</span><br><span class="line">    <span class="keyword">if</span> (deepTag.includes(type)) &#123;</span><br><span class="line">        cloneTarget = getInit(target, type);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 防止循环引用</span></span><br><span class="line">    <span class="keyword">if</span> (map.get(target)) &#123;</span><br><span class="line">        <span class="keyword">return</span> map.get(target);</span><br><span class="line">    &#125;</span><br><span class="line">    map.set(target, cloneTarget);</span><br><span class="line">    <span class="comment">// 克隆set</span></span><br><span class="line">    <span class="keyword">if</span> (type === setTag) &#123;</span><br><span class="line">        target.forEach(<span class="function"><span class="params">value</span> =&gt;</span> &#123;</span><br><span class="line">            cloneTarget.add(clone(value,map));</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> cloneTarget;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 克隆map</span></span><br><span class="line">    <span class="keyword">if</span> (type === mapTag) &#123;</span><br><span class="line">        target.forEach(<span class="function">(<span class="params">value, key</span>) =&gt;</span> &#123;</span><br><span class="line">            cloneTarget.set(key, clone(value,map));</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> cloneTarget;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 克隆对象和数组</span></span><br><span class="line">    <span class="keyword">const</span> keys = type === arrayTag ? <span class="literal">undefined</span> : <span class="built_in">Object</span>.keys(target);</span><br><span class="line">    forEach(keys || target, (value, key) =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (keys) &#123;</span><br><span class="line">            key = value;</span><br><span class="line">        &#125;</span><br><span class="line">        cloneTarget[key] = clone(target[key], map);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> cloneTarget;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="3、判断数据类型"><a href="#3、判断数据类型" class="headerlink" title="3、判断数据类型"></a>3、判断数据类型</h2><p><strong>一般使用typeof、instanceof，或者使用object.tostring方法，从而判断数据类型。</strong></p>
<p>typeof 是一个操作符，其右侧跟一个一元表达式，并返回这个表达式的数据类型。对于null、array不能判断。</p>
<p>instanceof 可以正确判断对象的数据类型，用来判断 A 是否为 B 的实例;内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。</p>
<p>toString()：是 Object 的原型方法，调用该方法，默认返回当前对象的 [[Class]] 。</p>
<p>这是一个内部属性，其格式为 [object xxx] ，其中 xxx 就是对象的类型。对于 Object 对象，直接调用 toString() 就能返回 [object Object] 。而对于其他对象，则需要通过 call / apply 来调用才能返回正确的类型信息。很多时候</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="number">1</span>);  <span class="comment">//'[object Number]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="string">'1'</span>); <span class="comment">//'[object String]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="literal">NaN</span>); <span class="comment">//'[object Number]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(foo);  <span class="comment">//'[object Function]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]); <span class="comment">//'[object Array]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="literal">undefined</span>); <span class="comment">//'[object Undefined]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="literal">null</span>); <span class="comment">//'[object Null]'</span></span><br><span class="line"><span class="built_in">Object</span>.prototype.toString.call(<span class="literal">true</span>); <span class="comment">//'[object Boolean]'</span></span><br></pre></td></tr></table></figure>

<h2 id="4、数组常见API"><a href="#4、数组常见API" class="headerlink" title="4、数组常见API"></a>4、数组常见API</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr3 = arr1.concat(arr2);<span class="comment">//合并且返回副本</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> bar = [<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>];</span><br><span class="line"><span class="built_in">Array</span>.from(bar);<span class="comment">//from()方法返回一个新的数组迭代器对象，该对象包含数组中每个索引的键/值对</span></span><br><span class="line"><span class="keyword">let</span> dp = <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Array</span>(length1), ()=&gt; &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="built_in">Array</span>(length2).fill(<span class="number">0</span>);</span><br><span class="line">    &#125;);<span class="comment">//快速定义二维数组</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//find()方法返回满足回调条件的数组中第一个元素的值。否则将返回未定义</span></span><br><span class="line"><span class="comment">//forEach()方法遍历数组,foreach中this指向为undefine</span></span><br><span class="line"><span class="comment">//indexOf()；includes()；均用于查询数组中是否包含某个元素</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//join()将数组中的所有元素(或类似数组的对象)连接到一个字符串中，默认为逗号</span></span><br><span class="line"><span class="keyword">var</span> a = [<span class="string">'Wind'</span>, <span class="string">'Rain'</span>, <span class="string">'Fire'</span>];</span><br><span class="line">a.join();    <span class="comment">// 'Wind,Rain,Fire'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//splice()方法通过删除现有元素和/或添加新元素来更改数组的内容。</span></span><br><span class="line">array.splice(start, deleteCount, item1, item2);</span><br><span class="line">myFish.splice(<span class="number">2</span>, <span class="number">1</span>); <span class="comment">// remove 1 item at 2-index position (that is, "drum")</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//slice()切片</span></span><br><span class="line"><span class="built_in">Array</span>.prototype.slice();<span class="comment">//取出任意元素，参数一：从哪开始；参数二：(可选)结束位置。</span></span><br></pre></td></tr></table></figure>

<h2 id="5、手写bind、call、apply"><a href="#5、手写bind、call、apply" class="headerlink" title="5、手写bind、call、apply"></a>5、手写bind、call、apply</h2><p><strong>相同点：均用于改变函数的上下文，即this指向。</strong></p>
<p>fn.bind： 不会立即调用，而是返回一个绑定后的新函数。fn.call：立即调用，返回函数执行结果，this指向第一个参数，后面可有多个参数，并且这些都是fn函数的参数。fn.apply：立即调用，返回函数的执行结果，this指向第一个参数，第二个参数是个数组，这个数组里内容是fn函数的参数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//手写call</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.myCall = <span class="function"><span class="keyword">function</span> (<span class="params">thisArg, ...args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> fn = <span class="built_in">Symbol</span>(<span class="string">"fn"</span>) <span class="comment">// 声明一个独有的Symbol属性, 防止fn覆盖已有属性</span></span><br><span class="line">  thisArg = thisArg || <span class="built_in">window</span> <span class="comment">// 若没有传入this, 默认绑定window对象</span></span><br><span class="line">  thisArg[fn] = <span class="keyword">this</span> <span class="comment">//this指向调用者</span></span><br><span class="line">  <span class="keyword">const</span> result = thisArg[fn](...args) <span class="comment">//执行当前函数</span></span><br><span class="line">  <span class="keyword">delete</span> thisArg[fn]</span><br><span class="line">  <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 手写apply (args传入一个数组的形式)，原理其实和call差不多，只是入参不一样</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.myApply = <span class="function"><span class="keyword">function</span> (<span class="params">thisArg, args = []</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> fn = <span class="built_in">Symbol</span>(<span class="string">"fn"</span>)</span><br><span class="line">  thisArg = thisArg || <span class="built_in">window</span></span><br><span class="line">  thisArg[fn] = <span class="keyword">this</span></span><br><span class="line">  <span class="comment">//虽然apply()接收的是一个数组，但在调用原函数时，依然要展开参数数组</span></span><br><span class="line">  <span class="comment">//可以对照原生apply()，原函数接收到展开的参数数组</span></span><br><span class="line">  <span class="keyword">const</span> result = thisArg[fn](...args)</span><br><span class="line">  <span class="keyword">delete</span> thisArg[fn]</span><br><span class="line">  <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//手写bind</span></span><br><span class="line"><span class="built_in">Function</span>.prototype.myBind = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> args = [...arguments]</span><br><span class="line">    <span class="keyword">let</span> ctx = args.length&gt;<span class="number">0</span> ? args.shift() : <span class="built_in">window</span></span><br><span class="line">    <span class="keyword">let</span> s = <span class="built_in">Symbol</span>() <span class="comment">// 生成一个唯一值</span></span><br><span class="line">    <span class="comment">// 在被劫持者对象属性中加入这个属性</span></span><br><span class="line">    ctx[s] = <span class="keyword">this</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">      <span class="keyword">let</span> res = ctx[s](...args) <span class="comment">// 定义了在特定的上下文运行的结果，当执行时就能得到这个特定上下文的结果。</span></span><br><span class="line">      <span class="keyword">delete</span> ctx[s]</span><br><span class="line">      <span class="keyword">return</span> res</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="6、手写new"><a href="#6、手写new" class="headerlink" title="6、手写new"></a>6、手写new</h2><p><strong>1、创建一个新对象；2、将构造函数与新对象在原型链中联系起来，即新对象的proto要指向构造函数的原型对象；3、执行构造函数，并将构造函数的初始属性都挂载在新对象上。</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_new</span> (<span class="params">constructor, ...args</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> obj = &#123;&#125;;</span><br><span class="line">    obj._proto_ = <span class="keyword">constructor</span>.prototype;</span><br><span class="line">    let result = <span class="keyword">constructor</span>.call(obj, ...args);</span><br><span class="line">    //在obj中执行构造函数,并将构造函数的属性挂载到对象上；</span><br><span class="line">    return (typeof result === 'object') ? result : obj;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="7、正确判断this"><a href="#7、正确判断this" class="headerlink" title="7、正确判断this"></a>7、正确判断this</h2><p><strong>核心原理：this的指向在函数定义的时候是确定不了的，只有函数执行的时候才能确定，this不是指向自身！this 就是一个指针，指向调用函数的对象。this最终指向调用它的对象；同样，不管有多少层级的调用，this也只是指向它上一级的对象。</strong></p>
<p>默认绑定：通常情况是函数的调用，此时的this绑定为默认状态，全局执行时指向全局对象window。</p>
<p>隐式绑定：函数的调用是在某个对象上触发的，即调用位置存在上下文对象；典型的形式为XXX.fun()。同样，在这样调用时，由于只有最后一层确定this指向的是什么，因此只绑定最后一层的this。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">隐式绑定的问题，唯有XXX.fn()时才为隐式绑定</span></span><br><span class="line"><span class="comment">除此之外，隐式绑定的丢失可发生在回调函数中</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHi</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Hello,'</span>, <span class="keyword">this</span>.name);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person1 = &#123;</span><br><span class="line">    name: <span class="string">'YvetteLau'</span>,</span><br><span class="line">    sayHi: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="string">'Hello,'</span>,<span class="keyword">this</span>.name);</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> person2 = &#123;</span><br><span class="line">    name: <span class="string">'Christina'</span>,</span><br><span class="line">    sayHi: sayHi</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> name=<span class="string">'Wiliam'</span>;</span><br><span class="line">person1.sayHi();</span><br><span class="line">setTimeout(person2.sayHi,<span class="number">100</span>);</span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    person2.sayHi();</span><br><span class="line">&#125;,<span class="number">200</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">结果为:</span></span><br><span class="line"><span class="comment">Hello, Wiliam</span></span><br><span class="line"><span class="comment">Hello, Wiliam</span></span><br><span class="line"><span class="comment">Hello, Christina</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">第一条输出很容易理解，setTimeout的回调函数中，this使用的是默认绑定，非严格模式下，执行的是全局对象.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">第二条输出是不是有点迷惑了？说好XXX.fun()的时候，fun中的this指向的是XXX呢，为什么这次却不是这样了！Why?</span></span><br><span class="line"><span class="comment">其实这里我们可以这样理解: setTimeout(fn,delay)&#123; fn(); &#125;,相当于是将person2.sayHi赋值给了一个变量，最后执行了变量，这个时候，sayHi中的this显然和person2就没有关系了。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">第三条虽然也是在setTimeout的回调中，但是我们可以看出，这是执行的是person2.sayHi()使用的是隐式绑定，因此这是this指向的是person2，跟当前的作用域没有任何关系。</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<p>显示绑定：即通过call、apply、bind的方式，显式地指定this所指向的对象。</p>
<p>new绑定：在js中，构造函数只是使用new操作符时被调用的函数，其实跟普通函数没有区别。任何一个函数都可以使用new调用，只有对于函数的构造调用。我们在使用new调用函数时，就会将新对象绑定在这个函数的this上。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHi</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">	</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> Hi = <span class="keyword">new</span> sayHi(<span class="string">'Yevtte'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'Hello,'</span>, Hi.name);</span><br></pre></td></tr></table></figure>

<p>箭头函数：箭头函数它和普通函数有一些区别，箭头函数没有自己的this，它的this继承于外层代码库中的this。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    hi: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    sayHi: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">                <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    say: <span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> hi = obj.hi();  <span class="comment">//输出obj对象，隐式绑定规则</span></span><br><span class="line">hi();               <span class="comment">//输出obj对象，执行的是箭头函数，因为箭头函数会往上级寻找this指向</span></span><br><span class="line"><span class="keyword">let</span> sayHi = obj.sayHi();</span><br><span class="line"><span class="keyword">let</span> fun1 = sayHi(); <span class="comment">//输出window；此时执行sayhi，会有隐式绑定的丢失，因为你实际上是在全局作用域执行的</span></span><br><span class="line">fun1();             <span class="comment">//输出window；执行箭头函数，指向外层代码</span></span><br><span class="line">obj.say();          <span class="comment">//输出window；执行箭头函数，往上找一层为window</span></span><br><span class="line"><span class="comment">//箭头函数的this不是静态的，始终继承于外层代码库中的this.</span></span><br></pre></td></tr></table></figure>

<p>如何准确地判断this指向的是什么？</p>
<ol>
<li>函数是否在new中调用(new绑定)，如果是，那么this绑定的是新创建的对象。</li>
<li>函数是否通过call,apply调用，或者使用了bind(即硬绑定)，如果是，那么this绑定的就是指定的对象。</li>
<li>函数是否在某个上下文对象中调用(隐式绑定)，如果是的话，this绑定的是那个上下文对象。一般是obj.foo()</li>
<li>如果以上都不是，那么使用默认绑定。如果在严格模式下，则绑定到undefined，否则绑定到全局对象。</li>
<li>如果把null或者undefined作为this的绑定对象传入call、apply或者bind，这些值在调用时会被忽略，实际应用的是默认绑定规则。</li>
<li>如果是箭头函数，箭头函数的this继承的是外层代码块的this。</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1、不管多少层调用，指向调用时的上一层对象。</span></span><br><span class="line"><span class="keyword">var</span> flora = &#123;</span><br><span class="line">    Animal:<span class="string">"a"</span>,</span><br><span class="line">    mars:&#123;</span><br><span class="line">        Animal:<span class="string">"b"</span>,</span><br><span class="line">        say:<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">            <span class="built_in">console</span>.log(<span class="keyword">this</span>.Animal);</span><br><span class="line">    	&#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">flora.mars.say();<span class="comment">//输出为b</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//2、定义时不决定this，指向调用时的对象</span></span><br><span class="line"><span class="keyword">var</span> flora = &#123;</span><br><span class="line">    Animal:<span class="string">"a"</span>,</span><br><span class="line">    mars:&#123;</span><br><span class="line">        Animal:<span class="string">"b"</span>,</span><br><span class="line">        say:<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">            <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">            <span class="built_in">console</span>.log(<span class="keyword">this</span>.Animal);</span><br><span class="line">    	&#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> pink = flora.mars.say;</span><br><span class="line">pink();<span class="comment">//输出分别为window和undefine，因为pink()在全局环境上调用，因此this为window。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//3、new会改变this的指向</span></span><br><span class="line"><span class="comment">/*但当构造函数有返回值时，又不一样了，</span></span><br><span class="line"><span class="comment">如果返回的是一个对象：this会指向哪个返回的对象。</span></span><br><span class="line"><span class="comment">如果返回的不是一个对象：this还是指向那个函数的实例 */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">flora</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.animal = <span class="string">"a"</span>;   </span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> blue = <span class="keyword">new</span> flora();</span><br><span class="line"><span class="built_in">console</span>.log(blue.animal); <span class="comment">//a</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">flora</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.animal = <span class="string">"a"</span>;   </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> blue = <span class="keyword">new</span> flora();</span><br><span class="line"><span class="built_in">console</span>.log(blue.animal); <span class="comment">//undefined</span></span><br></pre></td></tr></table></figure>

<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> number = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    number: <span class="number">3</span>,</span><br><span class="line">    fn: (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> number;</span><br><span class="line">        <span class="keyword">this</span>.number *= <span class="number">2</span>;</span><br><span class="line">        number = number * <span class="number">2</span>;</span><br><span class="line">        number = <span class="number">3</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">var</span> num = <span class="keyword">this</span>.number;</span><br><span class="line">            <span class="keyword">this</span>.number *= <span class="number">2</span>;</span><br><span class="line">            <span class="built_in">console</span>.log(num);</span><br><span class="line">            number *= <span class="number">3</span>;</span><br><span class="line">            <span class="built_in">console</span>.log(number);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;)()</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> myFun = obj.fn;</span><br><span class="line">myFun.call(<span class="literal">null</span>);</span><br><span class="line">obj.fn();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.number);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">我们来分析一下，这段代码的执行过程。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">在定义obj的时候，fn对应的闭包就执行了，返回其中的函数，执行闭包中代码时，显然应用不了new绑定(没有出现new 关键字)，硬绑定也没有(没有出现call,apply,bind关键字),隐式绑定有没有？很显然没有，如果没有XX.fn()，那么可以肯定没有应用隐式绑定，所以这里应用的就是默认绑定了，非严格模式下this绑定到了window上(浏览器执行环境)。【这里很容易被迷惑的就是以为this指向的是obj，一定要注意，除非是箭头函数，否则this跟词法作用域是两回事，一定要牢记在心】</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.number * = <span class="number">2</span>; <span class="comment">//window.number的值是10(var number定义的全局变量是挂在window上的)</span></span><br><span class="line">number = number * <span class="number">2</span>; <span class="comment">//number的值是NaN;注意我们这边定义了一个number，但是没有赋值，number的值是undefined;Number(undefined)-&gt;NaN</span></span><br><span class="line">number = <span class="number">3</span>;          <span class="comment">//number的值为3</span></span><br><span class="line"><span class="comment">//myFun.call(null);我们前面说了，call的第一个参数传null，调用的是默认绑定;</span></span><br><span class="line">fn: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> num = <span class="keyword">this</span>.number;</span><br><span class="line">    <span class="keyword">this</span>.number *= <span class="number">2</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(num);</span><br><span class="line">    number *= <span class="number">3</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(number);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//执行时:</span></span><br><span class="line"><span class="keyword">var</span> num = <span class="keyword">this</span>.number; <span class="comment">//num=10; 此时this指向的是window</span></span><br><span class="line"><span class="keyword">this</span>.number * = <span class="number">2</span>;     <span class="comment">//window.number = 20</span></span><br><span class="line"><span class="built_in">console</span>.log(num);      <span class="comment">//输出结果为10</span></span><br><span class="line">number *= <span class="number">3</span>;           <span class="comment">//number=9; 这个number对应的闭包中的number;闭包中的number的是3</span></span><br><span class="line"><span class="built_in">console</span>.log(number);   <span class="comment">//输出的结果是9</span></span><br><span class="line"><span class="comment">//obj.fn();应用了隐式绑定，fn中的this对应的是obj.</span></span><br><span class="line"><span class="keyword">var</span> num = <span class="keyword">this</span>.number;<span class="comment">//num = 3;此时this指向的是obj</span></span><br><span class="line"><span class="keyword">this</span>.number *= <span class="number">2</span>;     <span class="comment">//obj.number = 6;</span></span><br><span class="line"><span class="built_in">console</span>.log(num);     <span class="comment">//输出结果为3;</span></span><br><span class="line">number *= <span class="number">3</span>;          <span class="comment">//number=27;这个number对应的闭包中的number;闭包中的number的此时是9</span></span><br><span class="line"><span class="built_in">console</span>.log(number);  <span class="comment">//输出的结果是27</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*最后一步console.log(window.number);输出的结果是20</span></span><br><span class="line"><span class="comment">因此组中结果为:</span></span><br><span class="line"><span class="comment">10</span></span><br><span class="line"><span class="comment">9</span></span><br><span class="line"><span class="comment">3</span></span><br><span class="line"><span class="comment">27</span></span><br><span class="line"><span class="comment">20</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>



<h2 id="8、手写继承实现"><a href="#8、手写继承实现" class="headerlink" title="8、手写继承实现"></a>8、手写继承实现</h2><p><strong>使用原型链继承，继承了父类的属性和方法，但对于引用属性，由于传入的是其地址，因此多个指向的是同样的父类属性；使用构造函数来实现属性的继承，在子类中调用父类构造函数，从而现实例属性的继承，因此同样子类实例上的属性覆盖了父类的同名属性。</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">(1)、借助call来实现继承(构造函数继承)</span></span><br><span class="line"><span class="comment">这样写的时候子类虽然能够拿到父类的属性值，但是问题是父类原型对象中一旦存在方法那么子类无法继承。</span></span><br><span class="line"><span class="comment">通过使用call()或apply()方法，Parent构造函数在为Child的实例创建的新对象的上下文执行了，</span></span><br><span class="line"><span class="comment">就相当于新的Child实例对象上运行了Parent()函数中的所有初始化代码，结果就是每个实例都有自己的info属性。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent1'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    Parent1.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child1'</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="keyword">new</span> Child1);</span><br><span class="line"><span class="comment">/*(2)、原型链实现继承</span></span><br><span class="line"><span class="comment">将父类的实例作为子类的原型：父类的所有引用属性（info）会被所有子类共享，更改一个子类的引用属性，其他子类也会受影响</span></span><br><span class="line"><span class="comment">如下：两个child实例用的是一个原型对象，修改其中之一的play，会影响另一个的。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent2'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child2'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">Child2.prototype = <span class="keyword">new</span> Parent2();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> Child2());</span><br><span class="line"><span class="comment">/*(3)、组合继承：同时应用上面两种继承方法</span></span><br><span class="line"><span class="comment">使用原型链继承原型上的属性和方法，而通过构造函数继承实例属性，这样既可以把方法定义在原型上以实现重用，又可以让每个实例都有自己的属性 */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent3</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent3'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">  &#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child3</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    Parent3.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child3'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  Child3.prototype = <span class="keyword">new</span> Parent3();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> s3 = <span class="keyword">new</span> Child3();</span><br><span class="line">  <span class="keyword">var</span> s4 = <span class="keyword">new</span> Child3();</span><br><span class="line">  s3.play.push(<span class="number">4</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(s3.play, s4.play);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*(4)、寄生组合继承</span></span><br><span class="line"><span class="comment">上面的组合继承有2点需要简单优化，1、避免parent构造函数执行2次；2、子类的构造函数应该指向Child，而不是Parent */</span></span><br><span class="line"><span class="comment">//new创建一个对象，执行构造函数。Object.create相当于创建一个对象，但是不执行构造函数。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent5</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent5'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child5</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    Parent5.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child5'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">Child5.prototype = <span class="built_in">Object</span>.create(Parent5.prototype);</span><br><span class="line">Child5.prototype.constructor = Child5;</span><br></pre></td></tr></table></figure>

<h2 id="11、手写防抖与节流"><a href="#11、手写防抖与节流" class="headerlink" title="11、手写防抖与节流"></a>11、手写防抖与节流</h2><p><strong>防抖：即短时间内大量触发同一事件，只会执行一次函数。原理：设置一个定时器，约定在xx毫秒之后再触发事件处理器，每次·触发事件都会重新设置处理器，直至xx毫秒内无第二次操作</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">debounce</span> (<span class="params">func, await</span>) </span>&#123;</span><br><span class="line">    <span class="comment">//传入参数分别为需要添加防抖的事件函数，和防抖的时间间隔</span></span><br><span class="line">    <span class="keyword">let</span> timeout = &#123;&#125;;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">let</span> context = <span class="keyword">this</span>;</span><br><span class="line">        <span class="keyword">let</span> args = <span class="built_in">arguments</span>;</span><br><span class="line">        <span class="keyword">if</span> (timeout) clearTimeout(timeout);<span class="comment">//每次触发都会重置定时器</span></span><br><span class="line">        timeout = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">            func.apply(context, args);</span><br><span class="line">        &#125;, <span class="keyword">await</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>节流：间隔执行，每隔一段时间就执行一次。原理：设置一个定时器，约定xx毫秒后执行时间，如果时间到了，那么执行函数并重置定时器，和防抖的区别是：防抖每次触发事件都重置定时器，而节流在定时器到事件后再清空。</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">throttle</span>(<span class="params">func, wait</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> timeout = <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">let</span> context = <span class="keyword">this</span>;</span><br><span class="line">        <span class="keyword">let</span> args = <span class="built_in">arguments</span>;</span><br><span class="line">        <span class="keyword">if</span> (!timeout) &#123;</span><br><span class="line">            timeout = setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">                timeout = <span class="literal">null</span>;</span><br><span class="line">                func.apply(context, args);</span><br><span class="line">            &#125;, wait);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="12、Array-sort"><a href="#12、Array-sort" class="headerlink" title="12、Array.sort()"></a>12、Array.sort()</h2><p>默认情况下，sort()方法按升序排列数组项；且会调用每个数组项的toString()方法，比较字符串；因此直接用于数字比较不会正确。sort()可以接受一个函数作为参数，该函数可以接收两个参数，1、第一个参数应该位于第二个之前，则返回负数；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> values = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">10</span>, <span class="number">15</span>];</span><br><span class="line">values.sort();</span><br><span class="line">alert(values);<span class="comment">//0, 1, 10 ,15 ,5 字符串比较时，10在5的前面，因此数组顺序被改变</span></span><br><span class="line">values.sort(<span class="function"><span class="keyword">function</span> <span class="title">compare</span> (<span class="params">value1, value2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> value1 - value2;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//当value1小于value2时，返回负数，小的放在前面，因此为升序排列。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*这里的话，我们顺便再手写一个快速排序吧</span></span><br><span class="line"><span class="comment">直接写一个较优的写法，主要分两步，</span></span><br><span class="line"><span class="comment">一个partition函数用于递归处理partition过程，传入参数为array，以及递归处理过程的左右index</span></span><br><span class="line"><span class="comment">一个quicksort用于调用partition进行快排实现，以及一些边界的特殊情况处理</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> quickSort_New = <span class="function"><span class="keyword">function</span>(<span class="params">ary, left, right</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(left &gt;= right) &#123;</span><br><span class="line">            <span class="keyword">return</span> ary;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> i = left,</span><br><span class="line">        <span class="keyword">var</span> j = right;</span><br><span class="line">        <span class="keyword">var</span> base = ary[left];</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (i &lt; j) &#123;</span><br><span class="line">            <span class="comment">// 从右边起，寻找比基数小的数</span></span><br><span class="line">            <span class="keyword">while</span> (i&lt;j &amp;&amp; ary[j] &gt;= base) &#123;</span><br><span class="line">                j--;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 从左边起，寻找比基数大的数</span></span><br><span class="line">            <span class="keyword">while</span> (i&lt;j &amp;&amp; ary[i] &lt;= base) &#123;</span><br><span class="line">                i++</span><br><span class="line">            &#125; </span><br><span class="line">            <span class="keyword">if</span> (i &lt; j) &#123;</span><br><span class="line">                <span class="keyword">var</span> temp = ary[i];</span><br><span class="line">                ary[i] = ary[j];</span><br><span class="line">                ary[j] = temp;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ary[left] = ary[i];</span><br><span class="line">        ary[i] = base;</span><br><span class="line">    	<span class="comment">//最后将base放至合适的位置</span></span><br><span class="line"></span><br><span class="line">        quickSort_New(ary, left, i<span class="number">-1</span>);</span><br><span class="line">        quickSort_New(ary, i+<span class="number">1</span>, right);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> ary;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//或者有下面这种实现方式</span></span><br><span class="line"><span class="keyword">var</span> devide_Xin = <span class="function"><span class="keyword">function</span> (<span class="params">array, start, end</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(start &gt;= end) <span class="keyword">return</span> array;</span><br><span class="line">        <span class="keyword">var</span> baseIndex = <span class="built_in">Math</span>.floor((start + end) / <span class="number">2</span>), <span class="comment">// 基数索引</span></span><br><span class="line">             i = start,</span><br><span class="line">             j = end;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">            <span class="keyword">while</span> (array[i] &lt; array[baseIndex]) &#123;</span><br><span class="line">                i++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">while</span> (array[j] &gt; array[baseIndex])  &#123;</span><br><span class="line">                j--;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(i &lt;= j) &#123;</span><br><span class="line">                <span class="keyword">var</span> temp = array[i];</span><br><span class="line">                array[i] = array[j];</span><br><span class="line">                array[j] = temp;</span><br><span class="line">                i++;</span><br><span class="line">                j--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> i;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> quickSort_Xin = <span class="function"><span class="keyword">function</span> (<span class="params">array, start, end</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(array.length &lt; <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> array;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> index = devide_Xin(array, start, end);</span><br><span class="line">        <span class="keyword">if</span>(start &lt; index <span class="number">-1</span>) &#123;</span><br><span class="line">            quickSort_Xin(array, start, index - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(end &gt; index) &#123;</span><br><span class="line">            quickSort_Xin(array, index, end);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> array;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>原理思路：基本上使用的是快速排序的方法。设要排序的元素个数是n：1、当 n &lt;= 10 时，采用插入排序；2、当 n &gt; 10 时，采用三路快速排序；3、10 &lt; n &lt;= 1000, 采用中位数作为哨兵元素；4、n &gt; 1000, 每隔 200~215 个元素挑出一个元素，放到一个新数组，然后对它排序，找到中间位置的数，以此作为中位数。</p>
<p>为什么要花这么大的力气选择哨兵元素？因为快速排序的性能瓶颈在于递归的深度，最坏的情况是每次的哨兵都是最小元素或者最大元素，那么进行partition(一边是小于哨兵的元素，另一边是大于哨兵的元素)时，就会有一边是空的，那么这么排下去，递归的层数就达到了n, 而每一层的复杂度是O(n)，因此快排这时候会退化成O(n^2)级别。</p>
<h2 id="13、手写Ajax请求"><a href="#13、手写Ajax请求" class="headerlink" title="13、手写Ajax请求"></a>13、手写Ajax请求</h2><p><strong>我们常用的 ajax 就是通过 XMLHttpRequest 对象实现的，这个对象有很多的属性和事件，在使用之前，我们需要先将它实例化.实例化后，我们就可以通过 xhr 来发起一个请求。</strong></p>
<p>xhr 具有一个 open 方法，这个方法的作用类似于初始化，并不会发起真正的请求；open 方法具有 5 个参数，但是常用的是前 3 个method： 请求方式 —— get / post；url：请求的地址；async：是否异步请求，默认为 true（异步）。send 方法发送请求，并接受一个可选参数，当请求方式为 post 时，可以将请求体的参数传入，当请求方式为 get 时，可以不传或传入 null，不管是 get 还是 post，参数都需要通过 encodeURIComponent 编码后拼接。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ajax</span>(<span class="params">url, fnSucc, fnFaild</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> xhttp;</span><br><span class="line">    <span class="comment">// 第一步：创建XMLHttpRequest对象</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">window</span>.XMLHttpRequest) &#123;</span><br><span class="line">        <span class="comment">// 现代浏览器</span></span><br><span class="line">        xhttp = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">     &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// IE6等老版本浏览器</span></span><br><span class="line">        xhttp = <span class="keyword">new</span> ActiveXObject(<span class="string">"Microsoft.XMLHTTP"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 第二步：调用XMLHttpRequest的open函数  </span></span><br><span class="line">    xhttp.open(<span class="string">'GET'</span>, url);</span><br><span class="line">    <span class="comment">// 第三步：XMLHttpRequest向服务器发送请求</span></span><br><span class="line">    xhttp.send();</span><br><span class="line">    <span class="comment">// 第四步：处理响应</span></span><br><span class="line">    xhttp.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="comment">//XMLHttpRequest上有一个方法，名叫onreadystatechange，当请求接收到响应的时候便会触发，在这个方法内部，我们来定义对响应的处理：</span></span><br><span class="line">      <span class="keyword">if</span> (xhttp.readyState === <span class="number">4</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (xhttp.status === <span class="number">200</span>) &#123;</span><br><span class="line">          fnSucc(xhttp.responseText)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (fnFaild) fnFaild(xhttp.responseText);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="14、手写发布订阅模式"><a href="#14、手写发布订阅模式" class="headerlink" title="14、手写发布订阅模式"></a>14、手写发布订阅模式</h2><p>发布订阅模式：又是观察者模式，定义对象的一对多的依赖关系，当一个对象的依赖发生改变时，所有依赖于它的对象都将得到通知。</p>
<p>一般我们会用传统的事件模型来代替发布订阅，事件的模式其实就是发布订阅模式。</p>
<p>发布订阅模式与观察者模式的不同，“第三者” （事件中心）出现。目标对象并不直接通知观察者，而是通过事件中心来派发通知。</p>
<p>基于一个事件（主题）通道，希望接收通知的对象 Subscriber 通过自定义事件订阅主题，被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先定义一个观察者observer，用于将两个对象间的关系解耦，</span></span><br><span class="line"><span class="keyword">let</span> observer = &#123;</span><br><span class="line">    <span class="comment">//定义收集订阅的集合</span></span><br><span class="line">    subscribes: &#123;&#125;,</span><br><span class="line">    <span class="comment">//定义订阅事件,参数两个，第一个为订阅的类型，第二个为触发该订阅后执行的回调函数.</span></span><br><span class="line">    subscribe: <span class="function"><span class="keyword">function</span> (<span class="params">person, fn</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>.subscribes[type]) &#123;</span><br><span class="line">            <span class="comment">//当对于该事件，还没有订阅任何触发的回调函数时，则新建数组来存储</span></span><br><span class="line">            <span class="comment">//这样，针对不同的订阅者，事件触发时可触发不同回调，产生不同结果</span></span><br><span class="line">            <span class="keyword">this</span>.subscribes[type] = [];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> fn === <span class="string">"function"</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.subscribes[type].push(fn);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">//定义发布事件，挨个地处理调用，且处理不存在的调用，第一个参数为发布的事件</span></span><br><span class="line">    <span class="comment">//第二个参数为传给触发订阅后执行的回调函数的参数，会根据订阅的顺序来进行挨个触发</span></span><br><span class="line">    publish: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">let</span> type = [].shife.call(<span class="built_in">arguments</span>);</span><br><span class="line">        <span class="keyword">let</span> fns = <span class="keyword">this</span>.subscribes[type];</span><br><span class="line">        <span class="keyword">let</span> (!fns || fns.length) &#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">            <span class="comment">//当没有相关的订阅，则直接return</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//挨个进行调用数组中保存的回调，参数为后续publish的传入参数</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; fns.length; i++) &#123;</span><br><span class="line">            fns[i].apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="comment">//删除订阅</span></span><br><span class="line">    remove: <span class="function"><span class="keyword">function</span> (<span class="params">type, fn</span>) </span>&#123;</span><br><span class="line">        <span class="comment">//当传入为type为undefined时，设置为全部删除</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> type === <span class="string">'undefined'</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.subscribes = [];</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">let</span> fns = <span class="keyword">this</span>.subscribes[type];</span><br><span class="line">        <span class="keyword">if</span> (!fns || !fns.length) &#123;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//当传入为fn为undefined时，设置为单个type全部删除</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> fn === <span class="string">'undefined'</span>) &#123;</span><br><span class="line">            fns.length = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">return</span> ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//挨个遍历，仅删除对应项</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; fns.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (fns[i] === fn) &#123;</span><br><span class="line">                fns.splice(i, <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">jobListForA</span>(<span class="params">jobs</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'A'</span>, jobs);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">jobListForB</span>(<span class="params">jobs</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'B'</span>, jobs);</span><br><span class="line">&#125;</span><br><span class="line">observer.subscribe(<span class="string">'job'</span>, jobListForA);<span class="comment">//订阅了A的笔试成绩</span></span><br><span class="line">observer.subscribe(<span class="string">'job'</span>, jobListForB);<span class="comment">//订阅了B的笔试成绩</span></span><br><span class="line">observer.subscribe(<span class="string">'exanminationA'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">score</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(score);</span><br><span class="line">&#125;);<span class="comment">//A订阅了测试成绩</span></span><br><span class="line">observer.subscribe(<span class="string">'exanminationB'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">score</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(score);</span><br><span class="line">&#125;);<span class="comment">//B订阅了测试成绩</span></span><br><span class="line">observer.subscribe(<span class="string">'interviewA'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">result</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(result);</span><br><span class="line">&#125;);</span><br><span class="line">observer.publish(<span class="string">'examinationA'</span>, <span class="number">100</span>);</span><br><span class="line">observer.publish(<span class="string">'examinationB'</span>, <span class="number">80</span>);</span><br><span class="line">observer.publish(<span class="string">'job'</span>, [<span class="string">'前端'</span>, <span class="string">'后端'</span>, <span class="string">'测试'</span>]);<span class="comment">//输出A、B的岗位</span></span><br></pre></td></tr></table></figure>

<p>下面的观察者模式是发布订阅模式的简化版</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 目标者类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Subject</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">    <span class="keyword">this</span>.observers = [];  <span class="comment">// 观察者列表</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 添加</span></span><br><span class="line">  add(observer) &#123;</span><br><span class="line">    <span class="keyword">this</span>.observers.push(observer);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 删除</span></span><br><span class="line">  remove(observer) &#123;</span><br><span class="line">    <span class="keyword">let</span> idx = <span class="keyword">this</span>.observers.findIndex(<span class="function"><span class="params">item</span> =&gt;</span> item === observer);</span><br><span class="line">    idx &gt; <span class="number">-1</span> &amp;&amp; <span class="keyword">this</span>.observers.splice(idx, <span class="number">1</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 通知</span></span><br><span class="line">  notify() &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> observer <span class="keyword">of</span> <span class="keyword">this</span>.observers) &#123;</span><br><span class="line">      observer.update();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 观察者类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Observer</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(name) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 目标对象更新时触发的回调</span></span><br><span class="line">  update() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`目标者通知我更新了，我是：<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例化目标者</span></span><br><span class="line"><span class="keyword">let</span> subject = <span class="keyword">new</span> Subject();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实例化两个观察者</span></span><br><span class="line"><span class="keyword">let</span> obs1 = <span class="keyword">new</span> Observer(<span class="string">'前端开发者'</span>);</span><br><span class="line"><span class="keyword">let</span> obs2 = <span class="keyword">new</span> Observer(<span class="string">'后端开发者'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 向目标者添加观察者</span></span><br><span class="line">subject.add(obs1);</span><br><span class="line">subject.add(obs2);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 目标者通知更新</span></span><br><span class="line">subject.notify();  </span><br><span class="line"><span class="comment">// 输出：</span></span><br><span class="line"><span class="comment">// 目标者通知我更新了，我是前端开发者</span></span><br><span class="line"><span class="comment">// 目标者通知我更新了，我是后端开发者</span></span><br></pre></td></tr></table></figure>



<h2 id="15、手写堆排序"><a href="#15、手写堆排序" class="headerlink" title="15、手写堆排序"></a>15、手写堆排序</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">堆排序的写法</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapSort</span>(<span class="params">arr</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> heapSize = arr.length;</span><br><span class="line">  buildHeap(arr);<span class="comment">//构造一个所有节点都满足arr[parent[i]] &gt; arr[i]的堆结构数组，这样就把值最大的那个节点换到了根节点</span></span><br><span class="line">  <span class="keyword">while</span>(heapSize &gt; <span class="number">1</span>) &#123; <span class="comment">//*1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//在当前树中，交换位于根节点的最大值和最后一个节点的值,这样就把最大值排在了最后一个节点，这样就排好了最大值</span></span><br><span class="line">    <span class="keyword">const</span> temp = arr[<span class="number">0</span>];</span><br><span class="line">    arr[<span class="number">0</span>]=arr[heapSize<span class="number">-1</span>];</span><br><span class="line">    arr[heapSize<span class="number">-1</span>] = temp;</span><br><span class="line"></span><br><span class="line">    heapSize--;<span class="comment">//当前树中最后一个节点已经排好了值，故后面就不用再考虑这个节点，故新的树的大小减一</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (heapSize&gt;<span class="number">1</span>) &#123;</span><br><span class="line">      heapify(arr, heapSize, <span class="number">0</span>);</span><br><span class="line">        <span class="comment">//上面的交换操作产生了新的根节点，新的根节点只是通过跟最后一个节点交换得到的值，故新的根节点不满足条件arr[parent[i]]&lt;arr[i]，所以要对根节点再次进行h</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description </span>构造一个所有节点都满足arr[parent[i]] &gt; arr[i]的堆结构数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;Array&#125;</span> </span>arr 待排序数组</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildHeap</span>(<span class="params">arr</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> heapSize = arr.length;</span><br><span class="line">  <span class="keyword">const</span> firstHeapifyIndex = <span class="built_in">Math</span>.floor(heapSize/<span class="number">2</span><span class="number">-1</span>);</span><br><span class="line">    <span class="comment">//从树的倒数第二层的最后一个有子节点的节点（对于满二叉树就是倒数第二层的最后一个节点）开始进行heapify处理。Math.floor(heapSize/2-1)就是这个最后一个有子节点的节点索引。</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i=firstHeapifyIndex; i &gt;= <span class="number">0</span>; i--) &#123;<span class="comment">//从0到firstHeapifyIndex都要进行heapify处理，才能把最大的那个节点换到根节点</span></span><br><span class="line">    heapify(arr, heapSize, i);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description </span>以数组arr的前heapSize个节点为树，对其中索引为i的节点向子节点进行替换，直到满足从i往下的子节点都有arr[parent[i]]&gt;=arr[i]</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>arr TYPE Array  待排序的数组</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>heapSize TYPE Number 待排序的数组中要作为当前树处理的从前往后数的节点个数，即待排序数组中前heapSize个点是要作为树来处理</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;*&#125;</span> </span>i TYPE Number arr数组中、heapSize长度的树中的当前要进行往子节点替换的节点的索引</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapify</span>(<span class="params">arr, heapSize, i</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> leftIndex = i * <span class="number">2</span> + <span class="number">1</span>;<span class="comment">//索引i的节点的左子节点索引</span></span><br><span class="line">  <span class="keyword">const</span> rightIndex = i * <span class="number">2</span> + <span class="number">2</span>;<span class="comment">//索引i的节点的右子节点索引</span></span><br><span class="line">  <span class="keyword">let</span> biggestValueIndex = i;</span><br><span class="line">  <span class="keyword">if</span> (leftIndex &lt; heapSize &amp;&amp; arr[leftIndex] &gt; arr[biggestValueIndex]) &#123;</span><br><span class="line">    <span class="comment">//节点的最大index为heapSize-1</span></span><br><span class="line">    <span class="comment">//注意：这两次比较要跟arr[biggestValueIndex]比较，不能跟arr[i]比较，因为biggestValueIndex是会在左右i之间更新的</span></span><br><span class="line">    biggestValueIndex = leftIndex; <span class="comment">//如果左子节点的值大于biggestValueIndex的值（此时就是根节点的值），那么更新biggestValueIndex为左子节点索引</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (rightIndex &lt; heapSize &amp;&amp; arr[rightIndex] &gt; arr[biggestValueIndex]) &#123;</span><br><span class="line">    biggestValueIndex = rightIndex;<span class="comment">//如果右子节点的值大于biggestValueIndex的值(此时可能是根节点的值，也可能是左子节点的值)，那么更新biggestValueIndex为右子节点索引</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (biggestValueIndex !== i) &#123; <span class="comment">//如果biggestValueIndex是左子节点索引或右子节点索引，那么交换根节点与biggestValueIndex节点的值</span></span><br><span class="line">    <span class="keyword">const</span> temp = arr[i];</span><br><span class="line">    arr[i] = arr[biggestValueIndex];</span><br><span class="line">    arr[biggestValueIndex] = temp;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//交换后，被交换的那个子节点（左子节点或右子节点）往下可能就不再满足[parent[i]]&gt;=arr[i]，所以要继续对biggestValueIndex进行heaify处理，即将biggestValueIndex可能需要和子节点进行值交换,直到树的这个分支到叶子节点都满足arr[parent[i]]&gt;=arr[i]</span></span><br><span class="line">    heapify(arr, heapSize, biggestValueIndex);<span class="comment">//要</span></span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/07/25/vue%E6%BA%90%E7%A0%81/vue%E5%AE%98%E7%BD%91/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/07/25/vue%E6%BA%90%E7%A0%81/vue%E5%AE%98%E7%BD%91/" class="post-title-link" itemprop="url">vue官方文档</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-07-25 12:18:39" itemprop="dateCreated datePublished" datetime="2021-07-25T12:18:39+08:00">2021-07-25</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:21:36" itemprop="dateModified" datetime="2021-11-03T16:21:36+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/VUE/" itemprop="url" rel="index"><span itemprop="name">VUE</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>其实Vue已经写过很多了，官网也看过不止一遍，但面试时还是有部分问题没有完美回答出来，这里抽空打算重新全面地再看一遍Vue官网，毕竟确实是前端框架中官方文档最为详细、深入的。这里，就不会记录基本的Vue用法之类的了，更多可能会是查漏补缺，会写一些看官网时发现自己遗漏的知识。</p>
<h1 id="Vue基础"><a href="#Vue基础" class="headerlink" title="Vue基础"></a>Vue基础</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><h3 id="动态参数"><a href="#动态参数" class="headerlink" title="动态参数"></a>动态参数</h3><p>从 2.6.0 开始，可以用方括号括起来的 JavaScript 表达式作为一个指令的参数：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">注意，参数表达式的写法存在一些约束，如之后的“对动态参数表达式的约束”章节所述。</span></span><br><span class="line"><span class="comment">--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-bind:</span>[<span class="attr">attributeName</span>]=<span class="string">"url"</span>&gt;</span> ... <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>这里的 <code>attributeName</code> 会被作为一个 JavaScript 表达式进行动态求值，求得的值将会作为最终的参数来使用。例如，如果你的 Vue 实例有一个 <code>data</code> property <code>attributeName</code>，其值为 <code>&quot;href&quot;</code>，那么这个绑定将等价于 <code>v-bind:href</code>。</p>
<p>同样地，你可以使用动态参数为一个动态的事件名绑定处理函数：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-on:</span>[<span class="attr">eventName</span>]=<span class="string">"doSomething"</span>&gt;</span> ... <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">/*</span><br><span class="line">在这个示例中，当 eventName 的值为 "focus" 时，v-on:[eventName] 将等价于 v-on:focus。</span><br><span class="line">*/</span><br></pre></td></tr></table></figure>

<h3 id="计算属性与侦听器"><a href="#计算属性与侦听器" class="headerlink" title="计算属性与侦听器"></a>计算属性与侦听器</h3><p>计算属性实现的功能，其实可以在表达式中调用方法来达到同样的效果，两种方式的最终结果确实是完全相同的。然而，不同的是<strong>计算属性是基于它们的响应式依赖进行缓存的</strong>。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 <code>message</code> 还没有发生改变，多次访问 <code>reversedMessage</code> 计算属性会立即返回之前的计算结果，而不必再次执行函数。</p>
<p>计算属性默认只有 getter，不过在需要时你也可以提供一个 setter：</p>
<p>这也同样意味着下面的计算属性将不再更新，因为 <code>Date.now()</code> 不是响应式依赖：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">computed: &#123;</span><br><span class="line">  now: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Date</span>.now()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>相比之下，每当触发重新渲染时，调用方法将<strong>总会</strong>再次执行函数。我们为什么需要缓存？假设我们有一个性能开销比较大的计算属性 <strong>A</strong>，它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 <strong>A</strong>。如果没有缓存，我们将不可避免的多次执行 <strong>A</strong> 的 getter！如果你不希望有缓存，请用方法来替代。</p>
<p>Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动：<strong>侦听属性</strong>。当你有一些数据需要随着其它数据变动而变动时，你很容易滥用 <code>watch</code>。然而，通常更好的做法是使用计算属性而不是命令式的 <code>watch</code> 回调。</p>
<p>计算属性通常用于结果的一对多，可以监听多个属性变化，并根据其变化进行修改。而watch则是，一个watch侦听一个修改项，当一个修改时则触发事件。</p>
<p><code>watch</code> 选项提供了一个更通用的方法，来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时，这个方式是最有用的。特别是在异步处理中，比如服务端与前端的双向通信中，watch最为常见。</p>
<h3 id="v-show与v-if"><a href="#v-show与v-if" class="headerlink" title="v-show与v-if"></a>v-show与v-if</h3><p><code>v-if</code> 是“真正”的条件渲染，因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。<code>v-show</code> 就简单得多——不管初始条件是什么，元素总是会被渲染，并且只是简单地基于 CSS 进行切换。</p>
<p>共同点：都能控制元素的显示和隐藏。</p>
<p>不同点：实现本质方法不同，v-show本质就是通过控制css中的display设置为none,控制隐藏，只会编译一次，不管初始条件是什么，元素总是会被渲染，并且只是简单地基于 CSS 进行切换；v-if是动态的向DOM树内添加或者删除DOM元素，若初始值为 false,就不会编译了。而且v-if不停地销毁和创建比较消耗性能。</p>
<p>一般来说，<code>v-if</code> 有更高的切换开销，而 <code>v-show</code> 有更高的初始渲染开销。因此，如果需要非常频繁地切换，则使用 <code>v-show</code> 较好；如果在运行时条件很少改变，则使用 <code>v-if</code> 较好。</p>
<p>当 <code>v-if</code> 与 <code>v-for</code> 一起使用时，<code>v-for</code> 具有比 <code>v-if</code> 更高的优先级。尽量不要一起去使用。</p>
<h3 id="v-for"><a href="#v-for" class="headerlink" title="v-for"></a>v-for</h3><p>在自定义组件上，你可以像在任何普通元素上一样使用 <code>v-for</code>，此时key是必须的然而，任何数据都不会被自动传递到组件里，因为组件有自己独立的作用域。为了把迭代数据传递到组件里，我们要使用 prop：</p>
<h3 id="数组双向绑定的方法"><a href="#数组双向绑定的方法" class="headerlink" title="数组双向绑定的方法"></a>数组双向绑定的方法</h3><p>Vue 将被侦听的数组的变更方法进行了包裹，所以它们也将会触发视图更新。这些被包裹过的方法包括：</p>
<ul>
<li><code>push()</code></li>
<li><code>pop()</code></li>
<li><code>shift()</code></li>
<li><code>unshift()</code></li>
<li><code>splice()</code></li>
<li><code>sort()</code></li>
<li><code>reverse()</code></li>
</ul>
<p>变更方法，顾名思义，会变更调用了这些方法的原始数组。相比之下，也有非变更方法，例如 <code>filter()</code>、<code>concat()</code> 和 <code>slice()</code>。它们不会变更原始数组，而<strong>总是返回一个新数组</strong>。当使用非变更方法时，可以用新数组替换旧数组：你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是，事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法，所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。</p>
<h3 id="事件修饰符"><a href="#事件修饰符" class="headerlink" title="事件修饰符"></a>事件修饰符</h3><p>在事件处理程序中调用 <code>event.preventDefault()</code> 或 <code>event.stopPropagation()</code> 是非常常见的需求。尽管我们可以在方法中轻松实现这点，但更好的方式是：方法只有纯粹的数据逻辑，而不是去处理 DOM 事件细节。</p>
<p>为了解决这个问题，Vue.js 为 <code>v-on</code> 提供了<strong>事件修饰符</strong>。之前提过，修饰符是由点开头的指令后缀来表示的。</p>
<ul>
<li><p><code>.stop</code></p>
</li>
<li><p><code>.prevent</code></p>
</li>
<li><p><code>.capture</code></p>
</li>
<li><p><code>.self</code></p>
</li>
<li><p><code>.once</code></p>
</li>
<li><p><code>.passive</code></p>
</li>
</ul>
 <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 阻止单击事件继续传播 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-on:click.stop</span>=<span class="string">"doThis"</span>&gt;</span><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 提交事件不再重载页面 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">v-on:submit.prevent</span>=<span class="string">"onSubmit"</span>&gt;</span><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 修饰符可以串联 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-on:click.stop.prevent</span>=<span class="string">"doThat"</span>&gt;</span><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 只有修饰符 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">v-on:submit.prevent</span>&gt;</span><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 添加事件监听器时使用事件捕获模式 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 即内部元素触发的事件先在此处理，然后才交由内部元素进行处理 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-on:click.capture</span>=<span class="string">"doThis"</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 只当在 event.target 是当前元素自身时触发处理函数 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 即事件不是从内部元素触发的 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-on:click.self</span>=<span class="string">"doThat"</span>&gt;</span>...<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="v-model"><a href="#v-model" class="headerlink" title="v-model"></a>v-model</h3><p><code>v-model</code> 本质上不过是语法糖。它负责监听用户的输入事件以更新数据，并对一些极端场景进行一些特殊处理。</p>
<p><code>v-model</code> 在内部为不同的输入元素使用不同的 property 并抛出不同的事件。同样，你也可以在组件中使用v-model。</p>
<ul>
<li>text 和 textarea 元素使用 <code>value</code> property 和 <code>input</code> 事件；</li>
<li>checkbox 和 radio 使用 <code>checked</code> property 和 <code>change</code> 事件；</li>
<li>select 字段将 <code>value</code> 作为 prop 并将 <code>change</code> 作为事件。</li>
</ul>
<h3 id="父子组件"><a href="#父子组件" class="headerlink" title="父子组件"></a>父子组件</h3><p>父级组件可以像处理 native DOM 事件一样通过 <code>v-on</code> 监听子组件实例的任意事件。同时子组件可以通过调用内建的 <a href="https://cn.vuejs.org/v2/api/#vm-emit" target="_blank" rel="noopener"><strong><code>$emit</code></strong> 方法</a>并传入事件名称来触发一个事件。事件名称相同时，则会触发父组件所监听的事件。</p>
<p>同时，通过这样的事件响应，可以使用 <code>$emit</code> 的第二个参数来传递数据值，这就是子组件向父组件传递数据。</p>
<h3 id="插槽"><a href="#插槽" class="headerlink" title="插槽"></a>插槽</h3><p>编译作用域：当你想在一个插槽中使用数据时，该插槽跟模板的其它地方一样可以访问相同的实例 property (也就是相同的“作用域”)。作为一条规则，请记住：<strong>父级模板里的所有内容都是在父级作用域中编译的；子模板里的所有内容都是在子作用域中编译的。</strong></p>
<p>具名插槽：在向具名插槽提供内容的时候，我们可以在一个 <code>&lt;template&gt;</code> 元素上使用 <code>v-slot</code> 指令，并以 <code>v-slot</code> 的参数的形式提供其名称。很容易理解，其实就是分别向不同插槽提供不同内容。</p>
<p>作用域插槽：目的是为了让插槽的内容能够访问子模板的数据。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">为了让 user 在父级的插槽内容中可用，我们可以将 user 作为 <span class="tag">&lt;<span class="name">slot</span>&gt;</span> 元素的一个 attribute 绑定上去：</span><br><span class="line"><span class="tag">&lt;<span class="name">span</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">slot</span> <span class="attr">v-bind:user</span>=<span class="string">"user"</span>&gt;</span></span><br><span class="line">    &#123;&#123; user.lastName &#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">slot</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">绑定在 <span class="tag">&lt;<span class="name">slot</span>&gt;</span> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中，我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字：</span><br><span class="line"><span class="tag">&lt;<span class="name">current-user</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">template</span> <span class="attr">v-slot:default</span>=<span class="string">"slotProps"</span>&gt;</span></span><br><span class="line">    &#123;&#123; slotProps.user.firstName &#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">current-user</span>&gt;</span></span><br><span class="line">在这个例子中，我们选择将包含所有插槽 prop 的对象命名为 slotProps，但你也可以使用任意你喜欢的名字。</span><br></pre></td></tr></table></figure>

<p>已被废弃的slot-scope：在 <code>&lt;template&gt;</code> 上使用特殊的 <code>slot</code> attribute，可以将内容从父级传给具名插槽。</p>
<p>在 <code>&lt;template&gt;</code> 上使用特殊的 <code>slot-scope</code> attribute，可以接收传递给插槽的 prop </p>
<h3 id="动态组件与异步组件"><a href="#动态组件与异步组件" class="headerlink" title="动态组件与异步组件"></a>动态组件与异步组件</h3><p>动态组件：我们之前在一个多标签的界面中使用 <code>is</code> attribute 来切换不同的组件：当在这些组件之间切换的时候，你有时会想保持这些组件的状态，以避免反复重渲染导致的性能问题。重新创建动态组件的行为通常是非常有用的，但是在这个案例中，我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题，我们可以用一个 <code>&lt;keep-alive&gt;</code> 元素将其动态组件包裹起来。</p>
<p>异步组件：在大型应用中，我们可能需要将应用分割成小一些的代码块，并且只在需要的时候才从服务器加载一个模块。为了简化，Vue 允许你以一个工厂函数的方式定义你的组件，这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数，且会把结果缓存起来供未来重渲染。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Vue.component(<span class="string">'async-example'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 向 `resolve` 回调传递组件定义</span></span><br><span class="line">    resolve(&#123;</span><br><span class="line">      template: <span class="string">'&lt;div&gt;I am async!&lt;/div&gt;'</span></span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>如你所见，这个工厂函数会收到一个 <code>resolve</code> 回调，这个回调函数会在你从服务器得到组件定义的时候被调用。你也可以调用 <code>reject(reason)</code> 来表示加载失败。这里的 <code>setTimeout</code> 是为了演示用的，如何获取组件取决于你自己。一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用：</p>
<h3 id="选取组件"><a href="#选取组件" class="headerlink" title="选取组件"></a>选取组件</h3><p>在每个 <code>new Vue</code> 实例的子组件中，其根实例可以通过 <code>$root</code> property 进行访问</p>
<p>和 <code>$root</code> 类似，<code>$parent</code> property 可以用来从一个子组件访问父组件的实例。它提供了一种机会，可以在后期随时触达父级组件，以替代将数据以 prop 的方式传入子组件的方式。</p>
<p>尽管存在 prop 和事件，有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的，你可以通过 <code>ref</code> 这个 attribute 为子组件赋予一个 ID 引用。现在在你已经定义了这个 <code>ref</code> 的组件里，你可以使用：this.$refs.usernameInput来访问子组件。甚至可以通过其父级组件定义方法：<code>$refs</code> 只会在组件渲染完成之后生效，并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 <code>$refs</code>。</p>
<h3 id="依赖注入"><a href="#依赖注入" class="headerlink" title="依赖注入"></a>依赖注入</h3><p>使用 <code>$parent</code> property 无法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地，它用到了两个新的实例选项：<code>provide</code> 和 <code>inject</code>。<code>provide</code> 选项允许我们指定我们想要<strong>提供</strong>给后代组件的数据/方法。</p>
<p>然后在任何后代组件里，我们都可以使用 <code>inject</code> 选项来接收指定的我们想要添加在这个实例上的 property：</p>
<p>你可以在<a href="https://codesandbox.io/s/github/vuejs/vuejs.org/tree/master/src/v2/examples/vue-20-dependency-injection" target="_blank" rel="noopener">这里</a>看到完整的示例。相比 <code>$parent</code> 来说，这个用法可以让我们在<em>任意</em>后代组件中访问 <code>getMap</code>，而不需要暴露整个 <code>&lt;google-map&gt;</code> 实例。这允许我们更好的持续研发该组件，而不需要担心我们可能会改变/移除一些子组件依赖的东西。同时这些组件之间的接口是始终明确定义的，就和 <code>props</code> 一样。</p>
<p>实际上，你可以把依赖注入看作一部分“大范围有效的 prop”，除了：</p>
<ul>
<li>祖先组件不需要知道哪些后代组件使用它提供的 property</li>
<li>后代组件不需要知道被注入的 property 来自哪里</li>
</ul>
<h3 id="循环引用"><a href="#循环引用" class="headerlink" title="循环引用"></a>循环引用</h3><p>组件是可以在它们自己的模板中调用自身的。不过它们只能通过 <code>name</code> 选项来做这件事。稍有不慎，递归组件就可能导致无限循环：</p>
<p>组件间循环引用：当你仔细观察的时候，你会发现这些组件在渲染树中互为对方的后代<em>和</em>祖先——一个悖论！当通过 <code>Vue.component</code> 全局注册组件的时候，这个悖论会被自动解开。如果你是这样做的，那么你可以跳过这里。</p>
<p>然而，如果你使用一个<em>模块系统</em>依赖/导入组件，例如通过 webpack 或 Browserify，你会遇到一个错误：为了解释这里发生了什么，我们先把两个组件称为 A 和 B。模块系统发现它需要 A，但是首先 A 依赖 B，但是 B 又依赖 A，但是 A 又依赖 B，如此往复。这变成了一个循环，不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题，我们需要给模块系统一个点，在那里“A <em>反正</em>是需要 B 的，但是我们不需要先解析 B。</p>
<h3 id="过渡与动画"><a href="#过渡与动画" class="headerlink" title="过渡与动画"></a>过渡与动画</h3>
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/07/22/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/07/22/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" class="post-title-link" itemprop="url">初识设计模式</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-07-22 19:42:38" itemprop="dateCreated datePublished" datetime="2021-07-22T19:42:38+08:00">2021-07-22</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:27:29" itemprop="dateModified" datetime="2021-11-03T16:27:29+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">设计模式</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="初识设计模式"><a href="#初识设计模式" class="headerlink" title="初识设计模式"></a>初识设计模式</h1><h2 id="设计模式原则"><a href="#设计模式原则" class="headerlink" title="设计模式原则"></a>设计模式原则</h2><h3 id="单一职责原则"><a href="#单一职责原则" class="headerlink" title="单一职责原则"></a>单一职责原则</h3><p><strong>单一职责原则(Single Responsibility Principle,SRP)：一个类只负责一个功能领域中的相应职责，或者可以定义为：就一个类而言，应该只有一个引起它变化的原因。</strong> 单一职责原则是实现高内聚、低耦合的指导方针，它是最简单但又最难运用的原则，需要设计人员发现类的不同职责并将其分离，而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。 </p>
<h3 id="开放封闭原则"><a href="#开放封闭原则" class="headerlink" title="开放封闭原则"></a>开放封闭原则</h3><p> <strong>开闭原则(Open-Closed Principle,OCP)：一个软件实体应当对扩展开放，对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。</strong> 开闭原则是面向对象的可复用设计的第一块基石，它是最重要的面向对象设计原则。在开闭原则的定义中，软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。 </p>
<p>为了满足开闭原则，需要对系统进行抽象化设计，抽象化是开闭原则的关键。在Java、C#等编程语言中，可以为系统定义一个相对稳定的抽象层，而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制，可以通过它们定义系统的抽象层，再通过具体类来进行扩展。如果需要修改系统的行为，无须对抽象层进行任何改动，只需要增加新的具体类来实现新的业务功能即可，实现在不修改已有代码的基础上扩展系统的功能，达到开闭原则的要求。 </p>
<p>因为xml和properties等格式的配置文件是纯文本文件，可以直接通过编辑器进行编辑，且无须编译，因此在软件开发中，一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件，而原有的Java代码或C#代码没有做任何修改，该系统即可认为是一个符合开闭原则的系统。 </p>
<h3 id="里氏代换原则"><a href="#里氏代换原则" class="headerlink" title="里氏代换原则"></a>里氏代换原则</h3><p><strong>里氏代换原则(Liskov Substitution Principle,LSP)：所有引用基类（父类）的地方必须能透明地使用其子类的对象。 正式由于子类型的可替换性，才使得使用父类类型的模块在无需修改的情况下就可以进行扩展。</strong></p>
<p>里氏代换原则是实现开闭原则的重要方式之一，由于使用基类对象的地方都可以使用子类对象，因此在程序中尽量使用基类类型来对对象进行定义，而在运行时再确定其子类类型，用子类对象来替换父类对象。在使用里氏代换原则时需要注意如下几个问题： </p>
<p>(1)：子类的所有方法必须在父类中声明，或子类必须实现父类中声明的所有方法。根据里氏代换原则，为了保证系统的扩展性，在程序中通常使用父类来进行定义，如果一个方法只存在子类中，在父类中不提供相应的声明，则无法在以父类定义的对象中使用该方法。 </p>
<p>(2)：在运用里氏代换原则时，尽量把父类设计为抽象类或者接口，让子类继承父类或实现父接口，并实现在父类中声明的方法，运行时，子类实例替换父类实例，可以很方便地扩展系统的功能，同时无须修改原有子类的代码，增加新的功能可以通过增加一个新的子类来实现。 </p>
<h3 id="依赖倒转原则"><a href="#依赖倒转原则" class="headerlink" title="依赖倒转原则"></a>依赖倒转原则</h3><p><strong>依赖倒转原则(Dependency Inversion Principle,DIP)：抽象不应该依赖于细节，细节应当依赖于抽象。换言之，要针对接口编程，而不是针对实现编程。（高层模块不应该依赖低层模块，两个都应该依赖抽象）。</strong></p>
<p>依赖倒转原则是面向对象设计的主要实现机制之一，它是系统抽象化的具体实现。 依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中，尽量引用层次高的抽象层类，即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明，以及数据类型的转换等，而不要用具体类来做这些事情。</p>
<p>为了确保该原则的应用，一个具体类应当只实现接口或抽象类中声明过的方法，而不要给出多余的方法，否则将无法调用到在子类中增加的新方法。 在实现依赖倒转原则时，需要针对抽象层编程，而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中，依赖注入是指当一个对象要与其他对象发生依赖关系时，通过抽象来注入所依赖的对象。</p>
<p>常用的注入方式有三种，分别是：构造注入，设值注入（Setter注入）和接口注入。构造注入是指通过构造函数来传入具体类的对象，设值注入是指通过Setter方法来传入具体类的对象，而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型，在运行时再传入具体类型的对象，由子类对象来覆盖父类对象。 依赖倒转其实可以说是面向对象设计的标志，如果编写的时候考虑的都是如何针对抽象编程而不是针对细节编程，即程序中所有的依赖关系都是终止于抽象类或者接口，则为面向对象的设计。 </p>
<h3 id="接口隔离原则"><a href="#接口隔离原则" class="headerlink" title="接口隔离原则"></a>接口隔离原则</h3><p><strong>接口隔离原则(Interface Segregation Principle,ISP)：使用多个专门的接口，而不使用单一的总接口，即客户端不应该依赖那些它不需要的接口。</strong> </p>
<p>根据接口隔离原则，当一个接口太大时，我们需要将它分割成一些更细小的接口，使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色，不干不该干的事，该干的事都要干。这里的“接口”往往有两种不同的含义：一种是指一个类型所具有的方法特征的集合，仅仅是一种逻辑上的抽象；另外一种是指某种语言具体的“接口”定义，有严格的定义和结构，比如Java语言中的interface。对于这两种不同的含义，ISP的表达方式以及含义都有所不同： </p>
<p>(1)： 当把“接口”理解成一个类型所提供的所有方法特征的集合的时候，这就是一种逻辑上的概念，接口的划分将直接带来类型的划分。可以把接口理解成角色，一个接口只能代表一个角色，每个角色都有它特定的一个接口，此时，这个原则可以叫做“角色隔离原则”。</p>
<p>(2)： 如果把“接口”理解成狭义的特定语言的接口，那么ISP表达的意思是指接口仅仅提供客户端需要的行为，客户端不需要的行为则隐藏起来，应当为客户端提供尽可能小的单独的接口，而不要提供大的总接口，比如实现中还有还有自己的一些封装的功能方法，而对于每一个接口实现类，可能这些功能代码可能不同。在面向对象编程语言中，实现一个接口就需要实现该接口中定义的所有方法，因此大的总接口使用起来不一定很方便，为了使接口的职责单一，需要将大接口中的方法根据其职责不同分别放在不同的小接口中，以确保每个接口使用起来都较为方便，并都承担某一单一角色。</p>
<p>接口应该尽量细化，同时接口中的方法应该尽量少，每个接口中只包含一个客户端（如子模块或业务逻辑类）所需的方法即可，这种机制也称为“定制服务”，即为不同的客户端提供宽窄不同的接口。 在使用接口隔离原则时，需要注意控制接口的粒度，接口不能太小，如果太小会导致系统中接口泛滥，不利于维护；接口也不能太大，太大的接口将违背接口隔离原则，灵活性较差，使用起来很不方便。 </p>
<h3 id="迪米特法则"><a href="#迪米特法则" class="headerlink" title="迪米特法则"></a>迪米特法则</h3><p><strong>迪米特法则(Law of Demeter,LoD)：又叫作最少知道原则（Least Knowledge Principle, LKP），一个软件实体应当尽可能少地与其他实体发生相互作用。</strong>就是说一个对象应当对其他对象有尽可能少的了解，不和陌生人说话。 如果两个类不必彼此直接通信，那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话，可以通过第三者转发这个调用。 </p>
<p>迪米特法则可降低系统的耦合度，使类与类之间保持松散的耦合关系。迪米特法则可以简单说成：talk only to your immediate friends。 对于LOD来说，又被解释为下面几种方式：一个软件实体应当尽可能少的与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识，而且局限于那些与本单位密切相关的软件单位。 迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖，因此，很容易使得系统的功能模块功能独立，相互之间不存在（或很少有）依赖关系。 </p>
<p>迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系，也希望能通过它的友元类来转达。因此，应用迪米特法则有可能造成的一个后果就是：系统中存在大量的中介类，这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。 设计模式的门面模式（Facade）和中介模式（Mediator），都是应用的例子。 </p>
<h2 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h2><h3 id="创建型模式"><a href="#创建型模式" class="headerlink" title="创建型模式"></a>创建型模式</h3><p>创建型模式的共同作用：创建模式抽象了实例化的过程，它们帮助一个系统独立于如何创建、组合和表示它们的哪些对象，将关于系统使用哪些具体的类信息封装起来，允许使用者用结构、功能差别很大的“产品”对象来配置系统，其配置可以是静态，也可以是动态的。</p>
<h4 id="简单工厂模式"><a href="#简单工厂模式" class="headerlink" title="简单工厂模式"></a>简单工厂模式</h4><p><strong>在实现功能中，为了确保代码的低耦合性，根据单一职责原则，一个类只负责一个功能领域中的相应职责。</strong>因此，在选择实例化对象以实现功能时，需要进行情况的判断，这个判断处于的地方就是工厂类。 工厂类的作用是：根据不同的情况，实例化出不同类的对象，从而实现相应的功能，同时确保了代码的高内聚、低耦合。  </p>
<h4 id="工厂方法模式"><a href="#工厂方法模式" class="headerlink" title="工厂方法模式"></a>工厂方法模式</h4><p><strong>工厂方法模式：定义一个用于创建对象的接口，用子类决定实例化哪一个类。</strong>工厂方法使一个类的实例化延迟到了其子类。 工厂方法模式模板结构：  工厂方法模式是对简单工厂模式的推广，使用多态性，但缺点是每添加一个产品就需要增加一个产品工厂的类，开发量较大。可利用反射来解决分支问题。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F.PNG" alt="工厂方法模式"></p>
<h4 id="抽象工厂模式"><a href="#抽象工厂模式" class="headerlink" title="抽象工厂模式"></a>抽象工厂模式</h4><p>工厂模式方法是定义一个用于创建对象的接口，让子类来决定应该实例化哪一个类，每一个类代表一个工厂。 <strong>抽象工厂模式：提供一个创建一系列相关或相互依赖对象的接口，而无需指定它们具体的类。</strong> </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F.PNG" alt="抽象工厂模式"></p>
<p> AbstractProductA和AbstractProductB是两个抽象产品，它们都有两种不同实现。而AbstractFactory是一个抽象工厂接口，包含所有产品创建对象的抽象方法。 其实通俗地来讲就是在工厂的层面再往上抽象一层，越往上层抽象，则相对依赖地就更少，同样，对于下层的依赖，使用反射的方法来进行依赖注入。</p>
<p>依赖注入：利用反射+配置文件实现数据访问程序。反射是指：在运行状态中，对于任意一个类都可以反射来获取程序在运行时刻的内部结构（如：属性与方法）。使用反射API的时候就好像在看一个Java类在水中的倒影一样。知道了Java类的内部 结构之后，就可以与它进行交互，包括创建新的对象和调用对象中的方法等。这种交互方式与直接在源代码中使用的效果是相同的，但是又额外提供了运行时刻的灵活性，即可扩展性好。使用反射的一个最大的弊端是性能较差。 </p>
<h4 id="建造者模式"><a href="#建造者模式" class="headerlink" title="建造者模式"></a>建造者模式</h4><p><strong>建造者模式：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。</strong>  上述的工厂模式是为了批量构建相似类型的对象，因此被称为工厂，而建造者模式是为了构建一个复杂对象的各个部件，保证对象的基本功能没有缺失，同时保证不同实例可实现不同特定功能。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F.PNG" alt="建造者模式"></p>
<p>Builder：是创建一个Product对象的各个部件指定的抽象接口，后续的建造类需实现该抽象类中定义的所有方法，这样保证了建造出产品的基本功能没有缺失。 ConcreteBuilder：具体建造者，实现Builder接口，构造、装配各个部件。 Director：指挥者，用来根据用户的需求来构建对象，即构建一个使用Builder接口的对象。 <strong>建造者模式使得建造代码与表示代码分离，由于建造者隐藏了该产品是如何组装的，所以如若需要改变一个产品的内部表示，只需要再定义一个具体建造者</strong>。 </p>
<h4 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h4><p><strong>原型模式：用原型实例制定创建对象的种类，并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象，且不需知道任何创建的细节。</strong></p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F.PNG" alt="原型模式"></p>
<p> 原型模式的结构图如上：抽象类的关键在于其Clone方法。  使用原型模式时，要注意下深拷贝与浅拷贝的问题。</p>
<h4 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h4><p><strong>单例模式：保证一个类仅有一个实例，并且提供一个访问它的全局访问点。通常我们可以让一个全局变量使得一个对象被访问，但它不能防止你实例化多个对象。一个最好的办法就是，让类自身负责保存它的唯一实例。</strong>这个类可以保证没有其他实例可以被创建，并且还可以提供一个访问该实例的方法。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.PNG" alt="单例模式"></p>
<h3 id="结构型模式"><a href="#结构型模式" class="headerlink" title="结构型模式"></a>结构型模式</h3><p>结构型模式往往是在系统构建好之后，如何去维护、处理其各个模块之间的依赖、耦合关系，如果以更加少耦合地情况给系统添加新的功能，同时确保各个模块之间的通信与数据处理。；</p>
<h4 id="适配器模式"><a href="#适配器模式" class="headerlink" title="适配器模式"></a>适配器模式</h4><p><strong>适配器模式：将一个类的接口转换成客户希望的另外一个接口，适配器模式使得原本由于接口不兼容而不能工作的类可以一起工作。</strong></p>
<p>应用环境：让接口不同的类通过适配后协同工作。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F.PNG" alt="适配器模式"></p>
<h4 id="桥接模式"><a href="#桥接模式" class="headerlink" title="桥接模式"></a>桥接模式</h4><p> <strong>桥接模式：将抽象部分与它的实现部分相分离，使他们都可以独立地变化。</strong>这里的分离并不是让抽象类与其派生类分离，实现指的是抽象类和它的派生类用来实现自己的对象。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F.PNG" alt="桥接模式"></p>
<p><strong>桥接模式的本质：实现系统可能有多角度分类，每一种分类都有可能变化，那么就把这种多角度分离出来让他们独立变化，减少之间的耦合。</strong>即把两个角色的继承关系改成组合关系，从而应对各自独立的变化。</p>
<h4 id="组合模式"><a href="#组合模式" class="headerlink" title="组合模式"></a>组合模式</h4><p><strong>组合模式：将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。</strong>  组合模式应用场景：需求中是体现部分与整体层次的结构时，以及你希望用户可以忽略组合对象与单个对象的不同，统一地使用组合结构中的所有对象时。 </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F.PNG" alt="组合模式"></p>
<h4 id="装饰模式"><a href="#装饰模式" class="headerlink" title="装饰模式"></a>装饰模式</h4><p><strong>装饰模式：动态地给一个对象添加一些额外的职责，就增加功能来说，装饰模式比生成子类更加灵活</strong>。 </p>
<p>装饰模式模板结构： (1)：Component类：定义一个对象接口，可以动态地添加职责，同时作为一个抽象接口，遵循依赖倒置原则。 (2)：ConcreteComponent类：继承与Component类，定义具体对象，可以给对象添加具体的职责； (3)：Decorator：装饰抽象类，继承了Component，且从外类来扩展Component类的功能；但对于Component来说，无需知道Decorator的存在。  </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E8%A3%85%E9%A5%B0%E5%99%A8%E6%A8%A1%E5%BC%8F.PNG" alt="装饰器模式"></p>
<p>装饰模式利用SetComponent来对对象进行包装，这样每个装饰对象的实现就和如何使用这个对象分离开了，每个装饰对象只关心自己的功能，不关心如何被添加到对象链中。 </p>
<p>装饰模式解析： (1)：装饰模式是为已有功能动态地添加等多功能的一种方式，当系统需要新功能时，往旧的类中添加新代码往往会增加主类的复杂程度。 (2)：装饰模式将每个要装饰的功能放在单独的类中，并让该类包装其所要装饰的对象，因此当需要执行特殊行为时，客户端代码可在运行时，有选择地使用装饰功能包装对象。</p>
<h4 id="外观模式-门面模式"><a href="#外观模式-门面模式" class="headerlink" title="外观模式(门面模式)"></a>外观模式(门面模式)</h4><p><strong>外观模式：为子系统中的一组接口提供一个一致的界面，此模式定义了一个高层接口，该接口使得该子系统更加容易使用。</strong>同时引入一个外观对象，让其为子系统提供了一个单一而简单的屏障。</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F.PNG" alt="外观模式"></p>
<p>外观模式应用场景：1、在设计阶段，应当有意识地将不同的两个层分离，例如MVVM架构，为不同的子系统间提供简单的接口； 2、开发阶段：子系统因为不断的重构演化而变得越来越复杂时，增加外观模式提供简单的接口，减少不同模块间的依赖； 3、维护遗留的大型系统时，该系统已经难以维护与扩展，因此可以为新系统设计一个外观类，来提供针对老系统的简单化接口；再让新系统与这个外观类进行交互，减少对老系统的维护成本。</p>
<h4 id="享元模式"><a href="#享元模式" class="headerlink" title="享元模式"></a>享元模式</h4><p><strong>享元模式：运用共享技术有效地支持大量细粒度的对象。  享元模式可以避免大量非常相似类的开销</strong>；</p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F.PNG" alt="享元模式"></p>
<p>在程序设计中，有时需要生成大量细粒度的类实例来表示的护具，若该实例除了几个参数基本相同；如果能把这些参数移到类实例的外面，在方法调用时再将他们传递进来，就可通过共享大幅降低单个实例的数目。 享元模式应用：如果一个应用程序使用了大量的对象，且大量的对象造成了很大的存储开销时，则可以考虑使用；还有就是大量的对象状态可由外部状态来控制，这样可用较少的共享对象取代多组对象。</p>
<h4 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h4><p><strong>代理模式：为其他对象提供一种代理以控制对这个对象的访问。</strong> 代理模式模板结构： (1)：Subject类：定义了RealSubject和Proxy的共用接口，这样就在任何使用RealSubject的地方均使用Proxy。 (2)：RealSubject类：定义Proxy所代表的真实实体。 (3)：Proxy类：保存一个引用使得代理可以访问实体，并提供一个与Subject的接口相同的接口，这样代理可以用来替代实体。  </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F.PNG" alt="代理模式"></p>
<p>代理模式应用： (1)：远程代理：为一个对象在不同的地址空间提供局部代表，例如：在应用程序的项目中加入一个Web引用； (2)：虚拟代理：根据需要创建开销大的对象，用于存放实例化需要很久的对象；例如：HTML网页加载图片的过程，通过虚拟代理替代真实图片； (3)：安全代理：用来控制真实对象访问时的权限 (4)：智能指引：当调用真实对象时，代理处理另外一些事。    </p>
<h3 id="行为型模式"><a href="#行为型模式" class="headerlink" title="行为型模式"></a>行为型模式</h3><h4 id="观察者模式"><a href="#观察者模式" class="headerlink" title="观察者模式"></a>观察者模式</h4><p><strong>观察者模式：又称为发布-订阅模式，定义了一对多的依赖关系，让多个观察者对象同时监听某一个主题对象。该主题对象在状态发生变化时，会通知所有观察者对象，使他们能够更新自己。</strong>  其实本质就是一个抽象观察者类、一个抽象通知者类，之后的具体实现都是对该类的继承实现。常用于一个对象改变需要同时改变其他对象的时候。 </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F.PNG" alt="观察者模式"></p>
<p>Subject类：抽象通知者；1、抽象主题应该提供接口，删除或增加观察者对象；2、提供Notify方法通知观察者对象； Observer类：抽象观察者；1、更新接口，在收到Notify通知时，更新个人状态； ConcreteSubject：具体通知者，存入通知的具体状态； ConcretrObserver：具体观察者，实现要求的更新接口，同时保存一个指向具体通知者的引用，可修改具体通知者的通知状态。 </p>
<p>观察者模式解析：系统分割为相互协作类时，需要维护相关对象间的一致性，但我们不希望为了维持一致性而使各类紧密耦合，这样维护、扩展、重用都会有问题。而观察者模式的关键在于Subject，一旦Subject状态怪变，所有Observer得到通知，而观察者间并无联系。关键在于解除耦合：让耦合的双方都依赖于抽象，而不是依赖于聚义，从而使各自变化不会影响另一边变化。 </p>
<p>事件委托实现：委托就是一种引用方法的类型。一旦为委托分配了方法，委托将与该方法具有完全相同的行为，委托方法的使用可以像其他任何方法一样，具有参数和返回值，可以看做是对函数的抽象。 一个委托可以搭载多个方法，所有方法被依次唤起，且其可以使得委托对象所搭载的方法并不需要属于同一个类。 </p>
<h4 id="模板方法模式"><a href="#模板方法模式" class="headerlink" title="模板方法模式"></a>模板方法模式</h4><p><strong>模板方法模式：定义一个操作中的算法的骨架，而将一些步骤延迟到子类中。</strong></p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F.PNG" alt="模板方法模式"></p>
<p>模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。  模板方法模式的本质：1、当不变的和可变的行为在方法的子类实现中混合在一起的时候，不变的行为就会在子类中重复出现。2、我们通过模板方法模式把这些行为搬移到单一的地方，这样就能够帮助子类拜托重复的不变行为的纠缠。 </p>
<h4 id="命令模式"><a href="#命令模式" class="headerlink" title="命令模式"></a>命令模式</h4><p><strong>命令模式：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或记录请求日志，以及支持可撤销的操作。</strong>  命令模式的应用：1、较容易设计命令队列；2、容易将命令计入日志；3、允许接受请求的一方决定是否要否决请求；4、容易实现请求撤销；5、容易新加命令类。 </p>
<p><img src="/../../image/%E5%88%9D%E8%AF%86%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F.PNG" alt="命令模式"></p>
<h4 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h4><p>面对对象的编程模式，并不是类越多越好，类的划分是为了封装，但分类的基础是抽象，具有相同属性和功能的对象的抽象集合才是类，我们需要先通过抽象分析找出集合，并将每个集合定义为类。每次需要扩展功能时，则添加新的抽象类，并利用简单工厂模式来判断使用哪个抽象类。 </p>
<p>但利用简单工厂模式来管理时，面对算法的时常变动，每次维护、扩展都需要重新修改、编译该工厂函数，这个时候就可以使用策略模式。 策略模式：定义了算法家族，并将其分别封装起来，让它们之间可以互相替换，此模式让算法的变化，不会影响到使用算法文档客户。 </p>
<p>策略模式模板结构： (1)：Strategy类：定义所有支持的算法的公共接口； (2)：ConcreteStrategy类：封装了具体的算法、行为，继承于Strategy； (3)：Context类：用一个ConcreteStrategy来配置，维护对一个Strategy对象的引用。在初始化时，传入具体的策略对象，从而根据具体的策略对象来调用具体的算法方法。  </p>
<p>策略模式解析： (1)：本质是一种定义一系列算法的方法，这些算法完成相同的工作，只是实现不同，其以相同方式调用所有算法，减少了使用者与算法的耦合； (2)：其Strategy类为Context定义了一系列的可供重用的算法或行为，通过这种继承方式，有助于析取算法的公共功能； (3)：策略模式简化了单元测试是，每个算法有其单独类，从而可通过接口单独测试。 (4)：本质上，策略模式其实是用于封装各种不同的规则，使其成为一个个独立的类，最终减少switch语句的应用。一般只要在分析过程中需要在不同的时间应用不同的业务规则，就可以考虑使用策略模式来处理变化。 </p>
<h4 id="状态模式"><a href="#状态模式" class="headerlink" title="状态模式"></a>状态模式</h4><p>状态模式：当一个对象的内在状态改变时，允许改变其行为，这个对象看起来像是改变了其类。 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时，把状态的判断逻辑转移到表示不同状态的一系列类中，可以将复杂的判断逻辑简化。  状态模式应用：当一个对象的行为取决于它的状态时，并且它必须在运行时刻根据状态改变它的行为时，就可以考虑状态模式了。由于与状态相关的代码都存在于某个ConcreteState中，所以定义新的子类可以容易地增加新的状态和转换。  </p>
<h4 id="备忘录模式"><a href="#备忘录模式" class="headerlink" title="备忘录模式"></a>备忘录模式</h4><p>备忘录模式：在不破坏封装性的前提下，捕获一个对象的内部状态，并在该对象之外保存这个状态，这样就可以在之后将该对象恢复到原先保存的状态。   </p>
<h4 id="组合模式-1"><a href="#组合模式-1" class="headerlink" title="组合模式"></a>组合模式</h4><p>组合模式：将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。  组合模式应用场景：需求中是体现部分与整体层次的结构时，以及你希望用户可以忽略组合对象与单个对象的不同，统一地使用组合结构中的所有对象时。 </p>
<h4 id="迭代器模式"><a href="#迭代器模式" class="headerlink" title="迭代器模式"></a>迭代器模式</h4><p>迭代器模式：提供一种方法顺序访问一个聚合对象中各个元素，且不暴露该对象的内部表示。   </p>
<h4 id="职责链模式"><a href="#职责链模式" class="headerlink" title="职责链模式"></a>职责链模式</h4><p>职责链模式：使得多个对象都有机会处理请求，从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链，并沿着该链传递请求，直到有一个对象处理它为止。  职责链的应用：使接受者与发送者都没有对方的明确信息，且链中的对象自己也并不知道链的结构，结果是职责链可简化对象的相互谅解，仅需要保持一个指向后继者的引用，而不需要保持所有的候选者引用。 </p>
<h4 id="中介者模式"><a href="#中介者模式" class="headerlink" title="中介者模式"></a>中介者模式</h4><p>中介者模式：用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显式地相互引用，从而使其耦合松散，而且可以独立地改变他们之间的交互。  中介者模式很容易在系统中运用，同样也很容易在系统中误用，当系统中出现了“多对多”交互复杂的对象群时，不要急于使用中介者，而是先反思系统设计是否合理。中介者的出现减少了各个部分的耦合，但同样将交互复杂性变为了中介者的复杂性，这使得中介者会变得比任一个模块更复杂。 </p>
<h4 id="解释器模式"><a href="#解释器模式" class="headerlink" title="解释器模式"></a>解释器模式</h4><p>解释器模式：给定一个语言，定义它的文法的一种表示，并定义一个解释器，用于解释语言中的句子。  例如：JSX解析器babel；vue的模板语法解释器template。</p>
<h4 id="访问者模式"><a href="#访问者模式" class="headerlink" title="访问者模式"></a>访问者模式</h4><p>访问者模式：表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。  访问者模式适用于数据结构相对稳定的系统，其目的是将处理从数据结构中分离出来，将其余作用于结构上的操作解耦。访问者模式的优点是：新增操作很容易，新增一个访问者即可，但缺点是新增新的数据结构就比较困难。例如：redux、vuex，这些组件状态管理工具。 */</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/07/17/vue%E6%BA%90%E7%A0%81/vue%E7%BB%84%E4%BB%B6%E5%8C%96/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/07/17/vue%E6%BA%90%E7%A0%81/vue%E7%BB%84%E4%BB%B6%E5%8C%96/" class="post-title-link" itemprop="url">vue组件化</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-07-17 12:18:39" itemprop="dateCreated datePublished" datetime="2021-07-17T12:18:39+08:00">2021-07-17</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:21:27" itemprop="dateModified" datetime="2021-11-03T16:21:27+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/VUE/" itemprop="url" rel="index"><span itemprop="name">VUE</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="表单组件"><a href="#表单组件" class="headerlink" title="表单组件"></a>表单组件</h1><p>简单地自己造一下轮子，实现一下vue的自定组件，这里主要先从form组件开始实践。</p>
<p>form组件需求分析：1、指定数据、校验规则；高内聚、低耦合；</p>
<p>2、这里为了实现form组件的输入功能，我们自定义一个input组件来实现；为保证低耦合性，仅使用并实现双向绑定基本功能。</p>
<p>3、校验的实现跟input分开，再自定义一个KFormItem组件来实现校验的功能。执行校验的组件为KFormItem组件，但通知父组件执行校验的是其子组件KINput，由于事件是谁派发谁监听，不能用this.$emit，需要用this.parent.$emit。之后，在KFormItrm组件上监听校验事件，执行具体校验。</p>
<h2 id="Input"><a href="#Input" class="headerlink" title="Input"></a>Input</h2><p>实现功能：维护数据；</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 自定义组件双向绑定：:value  @input --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- v-bind="$attrs"展开$attrs </span></span><br><span class="line"><span class="comment">$attrs/$listeners包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有</span></span><br><span class="line"><span class="comment">声明任何 prop 时，这里会包含所有父作用域的绑定 ( class 和 style 除外)，并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">:type</span>=<span class="string">"type"</span> <span class="attr">:value</span>=<span class="string">"value"</span> @<span class="attr">input</span>=<span class="string">"onInput"</span> <span class="attr">v-bind</span>=<span class="string">"$attrs"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript">  <span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">    inheritAttrs: <span class="literal">false</span>, <span class="comment">// 设置为false避免设置到根元素上，</span></span></span><br><span class="line">    props: &#123;</span><br><span class="line">      value: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">default</span>: <span class="string">''</span></span></span><br><span class="line">      &#125;,</span><br><span class="line">      type: &#123;</span><br><span class="line"><span class="javascript">        type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">default</span>: <span class="string">'text'</span></span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">      onInput(e) &#123;</span><br><span class="line"><span class="actionscript">        <span class="comment">// 派发一个input事件即可，相当于在原生input上面再封装一个实现了双向绑定的input</span></span></span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.$emit(<span class="string">'input'</span>, e.target.value)</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">        <span class="comment">// 通知父级执行校验</span></span></span><br><span class="line"><span class="actionscript">        <span class="keyword">this</span>.$parent.$emit(<span class="string">'validate'</span>)</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="KFormItem"><a href="#KFormItem" class="headerlink" title="KFormItem"></a>KFormItem</h2><p>KformItem组件包裹Input组件，同时在前面添加label标签；</p>
<p>实现功能：1、执行校验2、显示错误信息</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- label --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">v-if</span>=<span class="string">"label"</span>&gt;</span>&#123;&#123;label&#125;&#125;<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">slot</span>&gt;</span><span class="tag">&lt;/<span class="name">slot</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 校验信息显示 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"error"</span>&gt;</span>&#123;&#123;error&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="actionscript"><span class="comment">// Asyc-validator</span></span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> Schema <span class="keyword">from</span> <span class="string">"async-validator"</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line"><span class="actionscript">  inject: [<span class="string">"form"</span>],</span></span><br><span class="line">  data() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="actionscript">      error: <span class="string">""</span> <span class="comment">// error是空说明校验通过</span></span></span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">  props: &#123;</span><br><span class="line">    label: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">default</span>: <span class="string">""</span></span></span><br><span class="line">    &#125;,</span><br><span class="line">    prop: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">String</span></span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  mounted() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">this</span>.$on(<span class="string">"validate"</span>, () =&gt; &#123;</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">this</span>.validate();</span></span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    validate() &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 规则</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> rules = <span class="keyword">this</span>.form.rules[<span class="keyword">this</span>.prop];</span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 当前值</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> value = <span class="keyword">this</span>.form.model[<span class="keyword">this</span>.prop];</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">      <span class="comment">// 校验描述对象</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> desc = &#123; [<span class="keyword">this</span>.prop]: rules &#125;;</span></span><br><span class="line"><span class="actionscript">      <span class="comment">// 创建Schema实例</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> schema = <span class="keyword">new</span> Schema(desc);</span></span><br><span class="line"><span class="actionscript">      <span class="keyword">return</span> schema.validate(&#123; [<span class="keyword">this</span>.prop]: value &#125;, errors =&gt; &#123;</span></span><br><span class="line">        if (errors) &#123;</span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.error = errors[<span class="number">0</span>].message;</span></span><br><span class="line"><span class="actionscript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="actionscript">          <span class="comment">// 校验通过</span></span></span><br><span class="line"><span class="actionscript">          <span class="keyword">this</span>.error = <span class="string">""</span>;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="KForm"><a href="#KForm" class="headerlink" title="KForm"></a>KForm</h2><p>一层层地往上面的层级进行靠近，在KForm的层级，除了实现slot将下面的层级包裹，同样也要接受数据并处理。</p>
<p>最终目的是在KForm上除了接受数据模型model以外，还要声明校验规则rules，因此声明数据model、rules。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">slot</span>&gt;</span><span class="tag">&lt;/<span class="name">slot</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  provide() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="actionscript">      form: <span class="keyword">this</span></span></span><br><span class="line"><span class="actionscript">    &#125;;<span class="comment">//直接将组件自己传递出去，这样就能够在下面子组件拿到model、rules</span></span></span><br><span class="line">  &#125;,</span><br><span class="line">  props: &#123;</span><br><span class="line">    model: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="actionscript">      required: <span class="literal">true</span></span></span><br><span class="line">    &#125;,</span><br><span class="line">    rules: &#123;</span><br><span class="line"><span class="javascript">      type: <span class="built_in">Object</span></span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    validate(cb) &#123;</span><br><span class="line"><span class="actionscript">      <span class="comment">// 获取所有孩子KFormItem</span></span></span><br><span class="line"><span class="actionscript">      <span class="comment">// [resultPromise]</span></span></span><br><span class="line"><span class="actionscript">      <span class="keyword">const</span> tasks = <span class="keyword">this</span>.$children</span></span><br><span class="line"><span class="javascript">        .filter(<span class="function"><span class="params">item</span> =&gt;</span> item.prop) <span class="comment">// 过滤掉没有prop属性的Item</span></span></span><br><span class="line"><span class="javascript">        .map(<span class="function"><span class="params">item</span> =&gt;</span> item.validate());</span></span><br><span class="line"></span><br><span class="line"><span class="actionscript">      <span class="comment">// 统一处理所有Promise结果</span></span></span><br><span class="line"><span class="javascript">      <span class="built_in">Promise</span>.all(tasks)</span></span><br><span class="line"><span class="javascript">        .then(<span class="function"><span class="params">()</span> =&gt;</span> cb(<span class="literal">true</span>))</span></span><br><span class="line"><span class="javascript">        .catch(<span class="function"><span class="params">()</span> =&gt;</span> cb(<span class="literal">false</span>));</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="Index"><a href="#Index" class="headerlink" title="Index"></a>Index</h2><p>这样，在index层级里面，我们将所有的自定义组件封装起来，</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">template</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- &lt;ElementTest&gt;&lt;/ElementTest&gt; --&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- KForm --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">KForm</span> <span class="attr">:model</span>=<span class="string">"userInfo"</span> <span class="attr">:rules</span>=<span class="string">"rules"</span> <span class="attr">ref</span>=<span class="string">"loginForm"</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 用户名 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">KFormItem</span> <span class="attr">label</span>=<span class="string">"用户名"</span> <span class="attr">prop</span>=<span class="string">"username"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">KInput</span> <span class="attr">v-model</span>=<span class="string">"userInfo.username"</span> <span class="attr">placeholder</span>=<span class="string">"请输入用户名"</span>&gt;</span><span class="tag">&lt;/<span class="name">KInput</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">KFormItem</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 密码 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">KFormItem</span> <span class="attr">label</span>=<span class="string">"密码"</span> <span class="attr">prop</span>=<span class="string">"password"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">KInput</span> <span class="attr">type</span>=<span class="string">"password"</span> <span class="attr">v-model</span>=<span class="string">"userInfo.password"</span> <span class="attr">placeholder</span>=<span class="string">"请输入密码"</span>&gt;</span><span class="tag">&lt;/<span class="name">KInput</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">KFormItem</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 提交按钮 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">KFormItem</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"login"</span>&gt;</span>登录<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">KFormItem</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">KForm</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">template</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> ElementTest <span class="keyword">from</span> <span class="string">"@/components/form/ElementTest.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> KInput <span class="keyword">from</span> <span class="string">"@/components/form/KInput.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> KFormItem <span class="keyword">from</span> <span class="string">"@/components/form/KFormItem.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> KForm <span class="keyword">from</span> <span class="string">"@/components/form/KForm.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> Notice <span class="keyword">from</span> <span class="string">"@/components/Notice.vue"</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span></span><br><span class="line">  data() &#123;</span><br><span class="line"><span class="actionscript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line">      userInfo: &#123;</span><br><span class="line"><span class="actionscript">        username: <span class="string">"tom"</span>,</span></span><br><span class="line"><span class="actionscript">        password: <span class="string">""</span></span></span><br><span class="line">      &#125;,</span><br><span class="line">      rules: &#123;</span><br><span class="line"><span class="actionscript">        username: [&#123; required: <span class="literal">true</span>, message: <span class="string">"请输入用户名称"</span> &#125;],</span></span><br><span class="line"><span class="actionscript">        password: [&#123; required: <span class="literal">true</span>, message: <span class="string">"请输入密码"</span> &#125;]</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;,</span><br><span class="line">  components: &#123;</span><br><span class="line">    ElementTest,</span><br><span class="line">    KInput,</span><br><span class="line">    KFormItem,</span><br><span class="line">    KForm</span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    login() &#123;</span><br><span class="line"><span class="javascript">      <span class="keyword">this</span>.$refs[<span class="string">"loginForm"</span>].validate(<span class="function"><span class="params">valid</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="actionscript">        <span class="keyword">const</span> notice = <span class="keyword">this</span>.$create(Notice, &#123;</span></span><br><span class="line"><span class="actionscript">          title: <span class="string">""</span>,</span></span><br><span class="line"><span class="actionscript">          message: valid ? <span class="string">"请求登录!"</span> : <span class="string">"校验失败!"</span>,</span></span><br><span class="line">          duration: 2000</span><br><span class="line">        &#125;);</span><br><span class="line">        notice.show();</span><br><span class="line"><span class="actionscript">        <span class="comment">// if (valid) &#123;</span></span></span><br><span class="line"><span class="actionscript">        <span class="comment">//   alert("submit");</span></span></span><br><span class="line"><span class="actionscript">        <span class="comment">// &#125; else &#123;</span></span></span><br><span class="line"><span class="actionscript">        <span class="comment">//   console.log("error submit!");</span></span></span><br><span class="line"><span class="actionscript">        <span class="comment">//   return false;</span></span></span><br><span class="line"><span class="actionscript">        <span class="comment">// &#125;</span></span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">style</span> <span class="attr">scoped</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h1 id="VueRouter"><a href="#VueRouter" class="headerlink" title="VueRouter"></a>VueRouter</h1><h2 id="需求分析"><a href="#需求分析" class="headerlink" title="需求分析"></a>需求分析</h2><p>1、作为一个插件存在：实现VueRouter类和install方法</p>
<p>2、实现两个全局组件：router-view用于显示匹配组件内容，router-link用于跳转</p>
<p>3、监控url变化：监听hashchange或popstate事件</p>
<p>4、响应最新url：创建一个响应式的属性current，当它改变时获取对应组件并显示</p>
<h2 id="实现VueRouter插件"><a href="#实现VueRouter插件" class="headerlink" title="实现VueRouter插件"></a>实现VueRouter插件</h2><p>在toyRouter.js里面，我们需要实现一个插件，先创建VueRouter类，并且实现其install方法，该方法会在当前的Vue原型链上挂载$router。使用vue.mixin来混入生命周期中，保证每个vue均能实现。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">KVueRouter</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(options) &#123;</span><br><span class="line">    <span class="keyword">this</span>.$options = options</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.$options);</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">KVueRouter.install = <span class="function"><span class="keyword">function</span> (<span class="params">_Vue</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 保存构造函数，在KVueRouter里面使用</span></span><br><span class="line">  Vue = _Vue;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 挂载$router</span></span><br><span class="line">  <span class="comment">// 怎么获取根实例中的router选项,混入生命周期钩子即可，该生命周期钩子会在所有组件都执行一遍</span></span><br><span class="line">  <span class="comment">//为什么要用混入方式写？主要原因是Vue.use(VueRouter)代码在前，Router实例创建在后，而install逻辑又需要用到该实例</span></span><br><span class="line">  Vue.mixin(&#123;</span><br><span class="line">    beforeCreate() &#123;</span><br><span class="line">      <span class="comment">// 确保根实例的时候才执行，只有根实例有router选项</span></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.$options.router) &#123;</span><br><span class="line">        Vue.prototype.$router = <span class="keyword">this</span>.$options.router</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> KVueRouter</span><br></pre></td></tr></table></figure>

<h2 id="router-link"><a href="#router-link" class="headerlink" title="router-link"></a>router-link</h2><p>为实现功能，将其写成独立的组件，并在vueRouter的install方法中再将其引入。</p>
<p>router-link实现的功能是：解析用户输入的路由值，并重新渲染一个输入路由值对应的a标签。且在纯运行时的环境，不能使用template而需要用render函数来渲染页面，这里我们需要渲染一个a标签。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  props: &#123;</span><br><span class="line">    to: &#123;</span><br><span class="line">      type: <span class="built_in">String</span>,</span><br><span class="line">      required: <span class="literal">true</span></span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  render(h) &#123;</span><br><span class="line">    <span class="comment">// &lt;a href="#/about"&gt;abc&lt;/a&gt;</span></span><br><span class="line">    <span class="comment">// &lt;router-link to="/about"&gt;xxx&lt;/router-link&gt;，值是用户通过props传进来的，因此直接用this.to，前面拼上#为了使用方便</span></span><br><span class="line">    <span class="comment">// h(tag, data, children)</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.$slots);</span><br><span class="line">    <span class="keyword">return</span> h(<span class="string">'a'</span>, &#123; <span class="attr">attrs</span>: &#123; <span class="attr">href</span>: <span class="string">'#'</span> + <span class="keyword">this</span>.to &#125; &#125;, <span class="keyword">this</span>.$slots.default)</span><br><span class="line">    <span class="comment">// return &lt;a href=&#123;'#' + this.to&#125;&gt;&#123;this.$slots.default&#125;&lt;/a&gt;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="router-view"><a href="#router-view" class="headerlink" title="router-view"></a>router-view</h2><p>router-view实现的功能基本上是：根据当前的路由值，获取对应的component并渲染在当前位置。</p>
<p>1、需要监控路由的变化，且为了实现路由改变时，便实时刷新页面，因此需要设置当前路由值为响应式数据。</p>
<p>2、为了确保查找的快速，在新建VueRouter时就应该以用户输入路由配置，初始化一个hash表，这样每次获取当前路由值的component时，只用O(1)时间即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//上述两点应该在VueRouter类中实现，因此VueRouter更新为：</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">KVueRouter</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(options) &#123;</span><br><span class="line">    <span class="keyword">this</span>.$options = options</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>.$options);</span><br><span class="line">    <span class="comment">// 需要创建响应式的current属性，当当前路由路径发生变化时，则页面就会重新渲染，这就是响应式</span></span><br><span class="line">    <span class="comment">// 利用Vue提供的defineReactive做响应化</span></span><br><span class="line">    <span class="comment">// 这样将来current变化的时候，依赖的组件会重新render</span></span><br><span class="line">    Vue.util.defineReactive(<span class="keyword">this</span>, <span class="string">'current'</span>, <span class="string">'/'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// this.app = new Vue(&#123;</span></span><br><span class="line">    <span class="comment">//   data() &#123;</span></span><br><span class="line">    <span class="comment">//     return &#123;</span></span><br><span class="line">    <span class="comment">//       current: '/'</span></span><br><span class="line">    <span class="comment">//     &#125;</span></span><br><span class="line">    <span class="comment">//   &#125;</span></span><br><span class="line">    <span class="comment">// &#125;)</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 监控url变化，利用bind锁定this，避免你使用的onHashChange函数的this变成后面的window</span></span><br><span class="line">    <span class="built_in">window</span>.addEventListener(<span class="string">'hashchange'</span>, <span class="keyword">this</span>.onHashChange.bind(<span class="keyword">this</span>))</span><br><span class="line">    <span class="built_in">window</span>.addEventListener(<span class="string">'load'</span>, <span class="keyword">this</span>.onHashChange.bind(<span class="keyword">this</span>))<span class="comment">//除了路由修改以外，用户刷新页面也要有重新渲染</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建一个路由映射表</span></span><br><span class="line">    <span class="keyword">this</span>.routeMap = &#123;&#125;</span><br><span class="line">    options.routes.forEach(<span class="function"><span class="params">route</span> =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">this</span>.routeMap[route.path] = route</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  onHashChange() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">window</span>.location.hash);</span><br><span class="line">    <span class="keyword">this</span>.current = <span class="built_in">window</span>.location.hash.slice(<span class="number">1</span>);<span class="comment">//由于前面还有#号，因此需要slice切割一下</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>而在router-view页面要实现的功能就要简单些了，主要是从路由映射hsahMap中获取对应组件，这里同样要用render函数来渲染。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  render(h) &#123;</span><br><span class="line">    <span class="comment">//获取path对应的component</span></span><br><span class="line">    <span class="keyword">const</span> &#123;routeMap, current&#125; = <span class="keyword">this</span>.$router;<span class="comment">//从路由映射中找到对应的路由，用hash表来查找，快速找到结果</span></span><br><span class="line">    <span class="built_in">console</span>.log(routeMap,current);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> component = routeMap[current].component || <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">return</span> h(component)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="Vuex"><a href="#Vuex" class="headerlink" title="Vuex"></a>Vuex</h1><p>目的：集中管理数据、可预测的改变数据；类似于整个项目数据的大管家，确保整个程序的状态、数据能够保持同步的状态，维持稳定。</p>
<h2 id="需求分析-1"><a href="#需求分析-1" class="headerlink" title="需求分析"></a>需求分析</h2><p>1、实现一个插件：声明Store类，挂载$store</p>
<p>2、：创建响应式的state，保存mutations、actions和getters</p>
<p>3、实现commit根据用户传入type执行对应mutation</p>
<p>4、实现dispatch根据用户传入type执行对应action，同时传递上下文</p>
<p>5、实现getters，按照getters定义对state做派生</p>
<h2 id="实现store插件"><a href="#实现store插件" class="headerlink" title="实现store插件"></a>实现store插件</h2><p>声明Store、install方法，并创建一个响应式的state。同时利用存取器，避免用户直接去取state</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 保存构造函数引用，避免import</span></span><br><span class="line"><span class="keyword">let</span> Vue;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(options) &#123;</span><br><span class="line">    <span class="comment">// this.$options = options;</span></span><br><span class="line">    <span class="keyword">this</span>._mutations = options.mutations;</span><br><span class="line">    <span class="keyword">this</span>._actions = options.actions;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 响应化处理state</span></span><br><span class="line">    <span class="comment">// this.state = new Vue(&#123;</span></span><br><span class="line">    <span class="comment">//   data: options.state</span></span><br><span class="line">    <span class="comment">// &#125;)</span></span><br><span class="line">    <span class="keyword">this</span>._vm = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">      data: &#123;</span><br><span class="line">        <span class="comment">// 加两个$，Vue不做代理，因此对外部是隐藏的，不能直接去访问。</span></span><br><span class="line">        $$state: options.state</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 绑定commit、dispatch的上下文问store实例</span></span><br><span class="line">    <span class="keyword">this</span>.commit = <span class="keyword">this</span>.commit.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.dispatch = <span class="keyword">this</span>.dispatch.bind(<span class="keyword">this</span>)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 存取器， 用户通过store.state的方式来访问，这样避免用户直接修改state。</span></span><br><span class="line">    <span class="comment">//官方的实现是：使用一个watch去监听任何修改，一旦用户尝试修改，则直接报错。</span></span><br><span class="line">  <span class="keyword">get</span> state() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="keyword">this</span>._vm);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>._vm._data.$$state</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">set</span> state(v) &#123;</span><br><span class="line">    <span class="built_in">console</span>.error(<span class="string">'无法修改！'</span>);</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">install</span>(<span class="params">_Vue</span>) </span>&#123;</span><br><span class="line">  Vue = _Vue;</span><br><span class="line"></span><br><span class="line">  Vue.mixin(&#123;</span><br><span class="line">    beforeCreate() &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.$options.store) &#123;</span><br><span class="line">        Vue.prototype.$store = <span class="keyword">this</span>.$options.store</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Vuex</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  Store,</span><br><span class="line">  install</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="实现commit、dispatch方法"><a href="#实现commit、dispatch方法" class="headerlink" title="实现commit、dispatch方法"></a>实现commit、dispatch方法</h2><p>在写方法之前需要将this.commit、this.dispatch绑定为该store实例，在构造函数上加上：this.commit = this.commit.bind(this)；this.dispatch = this.dispatch.bind(this)。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Store</span> </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    在写方法之前需要将this.commit、this.dispatch绑定为该store实例，在构造函数上加上：</span></span><br><span class="line"><span class="comment">    this.commit = this.commit.bind(this);</span></span><br><span class="line"><span class="comment">    this.dispatch = this.dispatch.bind(this);</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">   </span><br><span class="line">  <span class="comment">// store.commit('add', 1)</span></span><br><span class="line">  <span class="comment">// type: mutation的类型</span></span><br><span class="line">  <span class="comment">// payload：载荷，是参数</span></span><br><span class="line">  commit(type, payload) &#123;</span><br><span class="line">    <span class="keyword">const</span> entry = <span class="keyword">this</span>._mutations[type]</span><br><span class="line">    <span class="keyword">if</span> (entry) &#123;</span><br><span class="line">      entry(<span class="keyword">this</span>.state, payload)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  dispatch(type, payload) &#123;</span><br><span class="line">    <span class="keyword">const</span> entry = <span class="keyword">this</span>._actions[type]</span><br><span class="line">    <span class="keyword">if</span> (entry) &#123;</span><br><span class="line">      entry(<span class="keyword">this</span>, payload)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="实现getters"><a href="#实现getters" class="headerlink" title="实现getters"></a>实现getters</h2>
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/07/16/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8%E7%9B%B8%E5%85%B3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/07/16/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8%E7%9B%B8%E5%85%B3/" class="post-title-link" itemprop="url">前端安全概论</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-07-16 21:28:16" itemprop="dateCreated datePublished" datetime="2021-07-16T21:28:16+08:00">2021-07-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:31:47" itemprop="dateModified" datetime="2021-11-03T16:31:47+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/" itemprop="url" rel="index"><span itemprop="name">学校项目应用</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="前端安全概论"><a href="#前端安全概论" class="headerlink" title="前端安全概论"></a>前端安全概论</h1><h2 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h2><p>HTTP协议的以下几个特点决定了其安全性的不足：</p>
<p>1、无状态的；（需要利用session和cookie来进行状态管理）。其中<strong>cookie</strong>可由setMaxAge来设置生命周期，而cookie默认生命周期为会话级，只要关闭浏览器窗口就会消失。<strong>sessionStorage</strong>生命周期为当前窗口，sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭，即使刷新页面或者进入同源另一个页面，数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面，sessionStorage也是不一样的。<strong>localStoraged</strong>只要不被清除则永久存在。</p>
<p>2、明文传输的；（这里可以通过加密的方式来进行解决，包括对称加密与非对称加密）；</p>
<p>3、不验证通信方的身份；因此难以阻止海量请求的攻击，且难以防备CSRF；</p>
<p>4、无法证明报文的完整性；因此，在请求或响应送出之后直到对方接收之前的这段时间内，即使请求或响应的内容遭到篡改，也没有办法获悉。</p>
<p><strong>HTTPS</strong>，全称Hyper Text Transfer Protocol Secure，相比http，多了一个secure，也就是TLS（SSL），一个安全套接层。HTTPS 并非是应用层的一种新协议。只是 HTTP 通信接口部分用SSL（Secure Socket Layer）和 TLS（Transport Layer Security）协议代替而已。通常，HTTP 直接和 TCP 通信。当使用 SSL时，则演变成先和 SSL通信，再由 SSL和 TCP 通信了。简言之，所谓 HTTPS，其实就是身披SSL协议这层外壳的 HTTP，即又在TCP协议上加了一层。</p>
<h3 id="解决方式"><a href="#解决方式" class="headerlink" title="解决方式"></a>解决方式</h3><p>信任主机的问题.。 采用https 的server 必须从CA （数字证书认证机构处于客户端与服务器双方都可信赖的第三方机构的 立场上）申请一个用于证明服务器用途类型的证书，该证书有了CA的签名，客户端才能知道访问的服务器是安全的。 目前基本所有的在线购物和网银等网站或系统,关键部分应用都是https 的，客户通过信任该证书,从而信任了该主机，这样才能保证安全。</p>
<p>通讯过程中的数据的泄密和被窜改 使用https协议，服务端和客户端之间的所有通讯都是非对称加密的。客户端和服务端各有自己的一对非对称的密钥,一把叫做私有密钥（private key），另一把叫做公开密钥（public key），顾名思义，私有密钥不能让其他任何人知道，而公开密钥则可以随意发布，任何人都可以获得。使用公开密钥加密方式，发送密文的一方使用对方的公开密钥进行加密处理，对方收到被加密的信息后，再使用自己的私有密钥进行解密。利用这种方式，不需要发送用来解密的私有密钥，也不必担心密钥被攻击者窃听而盗走。</p>
<p><strong>简单点说就是：HTTP + 认证 + 加密 + 完整性保护 = HTTPS</strong></p>
<p>HTTPS采用的是对称加密和非对称加密两者并用的混合加密机制。若密钥能够实现安全交换，那么有可能会考虑仅使用公开密钥对称加密来通信。但是非对称加密与对称加密相比，其处理速度要慢。所以应充分利用两者各自的优势，将多种方法组合起来用于通信。在交换密钥环节使用非对称加密方式，之后的建立通信交换报文阶段则使用对称加密方式。由于密钥比较短，因此用慢的非对称加密也不会耗费过多时间。</p>
<h3 id="连接过程"><a href="#连接过程" class="headerlink" title="连接过程"></a>连接过程</h3><h4 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h4><p>这里我们顺便简要说一下TCP握手、挥手过程。TCP的字段和标志位如下：1、32 比特的序号字段和确认号字段，TCP 字节流每一个字节都按顺序编号。确认号是接收方期望从对方收到的下一字节的序号。2、ACK 标志位，用于指示确认字段中的值是有效的 ACK=1 有效，ACK=0 无效。3、SYN 标志位，用于连接建立，SYN 为 1 时，表明这是一个请求建立连接报文。4、FIN 标志位，用于连接拆除，FIN 为 1 时，表明发送方数据已发送完毕，并要求释放连接。</p>
<p>三次握手建立连接：1、客户端发送一个不包含应用层数据的 TCP 报文段，首部的 SYN 置为 1，随机选择一个初始序号（一般为 0）放在 TCP 报文段的序号字段中。（SYN 为 1 的时候，不能携带数据，但要消耗掉一个序号）</p>
<p>2、TCP 报文段到达服务器主机后，服务器提取报文段，并为该 TCP 连接分配缓存和变量。然后向客户端发送允许连接的 ACK 报文段（不包含应用层数据）。这个报文段的首部包含 4 个信息：ACK 置 为 1，SYN 置为 1；确认号字段置为客户端的序号 + 1；随机选择自己的初始序号（一般为 0）。</p>
<p>3、收到服务器的 TCP 响应报文段后，客户端也要为该 TCP 连接分配缓存和变量，并向服务器发送一个 ACK 报文段。这个报文段将服务器端的序号 + 1 放置在确认号字段中，用来对服务器允许连接的报文段进行响应，因为连接已经建立，所以 SYN 置为 0。最后一个阶段，报文段可以携带客户到服务器的数据。并且以后的每一个报文段，SYN 都置为 0。</p>
<p>四次挥手拆除连接：FIN 报文段即使不携带数据，也要消耗序号。1、客户端发送一个 FIN 置为 1 的报文段。2、服务器回送一个确认报文段。3、服务器发送 FIN 置为 1 的报文段。4、客户端回送一个确认报文段。</p>
<p>为什么是四次：1、当 A 给 B 发送 FIN 报文时，代表 A 不再发送报文，但仍可以接收报文。2、B 可能还有数据需要发送，因此先发送 ACK 报文，告知 A “我知道你想断开连接的请求了”。这样 A 便不会因为没有收到应答而继续发送断开连接的请求（即 FIN 报文）。3、B 在处理完数据后，就向 A 发送一个 FIN 报文，然后进入 LAST_ACK 阶段（超时等待）。4、A 向 B 发送 ACK 报文，双方都断开连接。</p>
<h4 id="HTTPS-1"><a href="#HTTPS-1" class="headerlink" title="HTTPS"></a>HTTPS</h4><p>1、<strong>客户端发起<code>HTTPS</code>请求</strong> 这个没什么好说的，就是用户在浏览器里输入一个<code>HTTPS</code>网址，然后连接到服务端的443端口。</p>
<p>2、<strong>服务端的配置</strong> 采用<code>HTTPS</code>协议的服务器必须要有一套数字证书，可以自己制作，也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过，才可以继续访问，而使用受信任的公司申请的证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥。如果对公钥不太理解，可以想象成一把钥匙和一个锁头，只是世界上只有你一个人有这把钥匙，你可以把锁头给别人，别人可以用这个锁把重要的东西锁起来，然后发给你，因为只有你一个人有这把钥匙，所以只有你才能看到被这把锁锁起来的东西。</p>
<p>3、<strong>传送证书</strong> 这个证书其实就是公钥，只是包含了很多信息，如证书的颁发机构，过期时间等等。</p>
<p>4、<strong>客户端解析证书</strong> 这部分工作是由客户端的SSL/TLS来完成的，首先会验证公钥是否有效，比如颁发机构，过期时间等等，如果发现异常，则会弹出一个警示框，提示证书存在的问题。如果证书没有问题，那么就生成一个<strong>随机值</strong>。然后用证书（也就是公钥）对这个随机值进行加密。就好像上面说的，把随机值用锁头锁起来，这样除非有钥匙，不然看不到被锁住的内容。</p>
<p>5、<strong>传送加密信息</strong> 这部分传送的是用证书加密后的随机值，目的是让服务端得到这个随机值，以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。</p>
<p>6、<strong>服务端解密信息</strong> 服务端用私钥解密后，得到了客户端传过来的随机值，然后把内容通过该随机值进行对称加密，将信息和私钥(随机值)通过某种算法混合在一起，这样除非知道私钥，不然无法获取内容，而正好客户端和服务端都知道这个私钥，所以只要加密算法够彪悍，私钥够复杂，数据就够安全。</p>
<p>7、<strong>传输加密后的信息</strong> 这部分信息就是服务端用私钥加密后的信息，可以在客户端用随机值解密还原。</p>
<p>8、<strong>客户端解密信息</strong> 客户端用之前生产的私钥解密服务端传过来的信息，于是获取了解密后的内容。整个过程第三方即使监听到了数据，也束手无策。</p>
<h2 id="HTTP版本"><a href="#HTTP版本" class="headerlink" title="HTTP版本"></a>HTTP版本</h2><h3 id="HTTP-1-1"><a href="#HTTP-1-1" class="headerlink" title="HTTP/1.1"></a>HTTP/1.1</h3><p>HTTP/1.1为网络效率做了大量的优化，最核心的有如下三种方式:</p>
<p>1、增加了持久连接；不用每进行一次HTTP通信都重新建立TCP连接了。</p>
<p>2.  浏览器为每个域名最多同时维护6个TCP持久连接; </p>
<p>3.  使用CDN的实现域名分片机制。</p>
<p>仍存在的问题：对带宽的利用率并不理想。原因有：1、TCP的慢启动，慢启动是TCP为了降低网络拥塞的策略；（会浪费项目的首屏渲染时间）；2、同时开启多条TCP连接时，这些连接会竞争固定的带宽。3、HTTP/1.1队头阻塞的问题：使用持久连接时，虽然公用一个TCP管道，但同一时刻只能处理一个请求。</p>
<h3 id="HTTP2改进"><a href="#HTTP2改进" class="headerlink" title="HTTP2改进"></a>HTTP2改进</h3><p>HTTP2主要想办法去规避TCP的慢启动和TCP连接之间的竞争问题。基于此，HTTP/2的思路就是一个域名只使用一个TCP⻓连接来传输数据，这样整个页面资源的下载过程只 需要一次慢启动，同时也避免了多个TCP连接竞争带宽所带来的问题。 </p>
<p>另外，就是队头阻塞的问题，等待请求完成后才能去请求下一个资源，这种方式无疑是最慢的，所以 HTTP/2需要实现资源的并行请求，也就是任何时候都可以将请求发送给服务器，而并不需要等待其他请求的完成，然后服务器也可以随时返回处理好的请求资源给浏览器。这就是HTTP2的多路复用机制：每个请求都有一个对应的ID，服务器接受之后根据喜好来决定优先返回哪些内容，可以将请求分成一帧一帧的数据去传输。</p>
<h4 id="多路复用的实现"><a href="#多路复用的实现" class="headerlink" title="多路复用的实现"></a>多路复用的实现</h4><p>HTTP/2添加了一个<strong>二进制分帧层</strong>，那我们就结合图来分析下HTTP/2的请求和接收过程。</p>
<p>1、首先，浏览器准备好请求数据，包括了请求行、请求头等信息，如果是POST方法，那么还要有请求体。2、这些数据经过二进制分帧层处理之后，会被转换为一个个带有请求ID编号的帧，通过协议栈将这些帧发送 给服务器。3、服务器接收到所有帧之后，会将所有相同ID的帧合并为一条完整的请求信息。4、然后服务器处理该条请求，并将处理的响应行、响应头和响应体分别发送至二进制分帧层。5、同样，二进制分帧层会将这些响应数据转换为一个个带有请求ID编号的帧，经过协议栈发送给浏览器。6、浏览器接收到响应帧之后，会根据ID编号将帧的数据提交给对应的请求。</p>
<p>从上面的流程可以看出，通过引入二进制分帧层，就实现了HTTP的多路复用技术。在这里虽然HTTP/2引入了二进制分帧层，不 过HTTP/2的语义和HTTP/1.1依然是一样的，也就是说它们通信的语言并没有改变，比如开发者依然可以通 过Accept请求头告诉服务器希望接收到什么类型的文件，依然可以使用Cookie来保持登录状态，依然可以使用Cache来缓存本地文件，这些都没有变，发生改变的只是传输方式。</p>
<h4 id="其他特性"><a href="#其他特性" class="headerlink" title="其他特性"></a>其他特性</h4><p>1、可以设置请求的优先级；2、服务器推送 除了设置请求的优先级外，HTTP/2还可以直接将数据提前推送到浏览器，这对首次打开⻚面的速度起到了至关 重要的作用。3、头部压缩 无论是HTTP/1.1还是HTTP/2，它们都有请求头和响应头，这是浏览器和服务器的通信语言。HTTP/2对请求 头和响应头进行了压缩，</p>
<h4 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h4><p>1、TCP的队头阻塞问题，在TCP传输过程中，由于单个数据包的丢失而造成的阻塞称为TCP上的队头阻塞。我们知道在HTTP/2中，多个请求是跑在一个TCP管道中的，如果其中任意一路数据流中出现了丢包的情况，那么就会阻塞该TCP连接中的所有请求。</p>
<p>2、TCP连接建立的延时。</p>
<h2 id="HTTP3改进"><a href="#HTTP3改进" class="headerlink" title="HTTP3改进"></a>HTTP3改进</h2><p>由于HTTP2存在一些严重的与TCP相关缺陷，解决方法是绕过TCP协议，基于UDP协议实现类似于 TCP的多路数据流、传输可靠性等功能，称为QUIC协议。</p>
<p>功能包括：1、实现了类似TCP的流量控制、传输可靠性的功能。虽然UDP不提供可靠性的传输，但QUIC在UDP的基础 之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性。</p>
<p>2、集成了TLS加密功能。目前QUIC使用的是TLS1.3，相较于早期版本，TLS1.3有更多的优点，其中最重要的 一点是减少了握手所花费的RTT个数。</p>
<p>3、实现了HTTP/2中的多路复用功能。和TCP不同，QUIC实现了在同一物理连接上可以有多个独立的逻辑数 据流(如下图)。实现了数据流的单独传输，就解决了TCP中队头阻塞的问题。</p>
<p>4、实现了快速握手功能。由于QUIC是基于UDP的，所以QUIC可以实现使用0-RTT或者1-RTT来建立连接， 这意味着QUIC可以用最快的速度来发送和接收数据，这样可以大大提升首次打开⻚面的速度。</p>
<h2 id="用户登录安全"><a href="#用户登录安全" class="headerlink" title="用户登录安全"></a>用户登录安全</h2><h3 id="Cookie-Session-登录"><a href="#Cookie-Session-登录" class="headerlink" title="Cookie + Session 登录"></a>Cookie + Session 登录</h3><p>步骤如下：1、用户访问 <code>a.com/pageA</code>，并输入密码登录。2、服务器验证密码无误后，会创建 SessionId，并将它保存起来。3、服务器端响应这个 HTTP 请求，并通过 Set-Cookie 头信息，将 SessionId 写入 Cookie 中。</p>
<p>第一次登录完成之后，后续的访问就可以直接使用 Cookie 进行身份验证了：1、用户访问 <code>a.com/pageB</code> 页面时，会自动带上第一次登录时写入的 Cookie。2、服务器端比对 Cookie 中的 SessionId 和保存在服务器端的 SessionId 是否一致。3、如果一致，则身份验证成功。</p>
<p>cookie验证存在的问题：1、由于服务器端需要对接大量的客户端，也就需要存放大量的 SessionId，这样会导致服务器压力过大。2、如果服务器端是一个集群，为了同步登录态，需要将 SessionId 同步到每一台机器上，无形中增加了服务器端维护成本。3、由于 SessionId 存放在 Cookie 中，所以无法避免 CSRF 攻击。</p>
<h3 id="Token登录"><a href="#Token登录" class="headerlink" title="Token登录"></a>Token登录</h3><p>Token 是服务端生成的一串字符串，以作为客户端请求的一个令牌。当第一次登录后，服务器会生成一个 Token 并返回给客户端，客户端后续访问时，只需带上这个 Token 即可完成身份认证。</p>
<p>步骤如下：1、用户输入账号密码，并点击登录。2、服务器端验证账号密码无误，创建 Token。3、服务器端将 Token 返回给客户端，由<em><strong>客户端自由保存</strong></em>。4、当后续不管是客户端访问接口时，还是跳转路由时，均需要在HTTP</p>
<p>最常见的 Token 生成方式是使用 JWT（Json Web Token）。</p>
<h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>SSO 单点登录，适用于中大型企业，想要统一内部所有产品的登录方式。</p>
<p>OAuth 第三方登录，简单易用，对用户和开发者都友好，但第三方平台很多，需要选择合适自己的第三方登录平台</p>
<h2 id="前端安全攻击"><a href="#前端安全攻击" class="headerlink" title="前端安全攻击"></a>前端安全攻击</h2>
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/07/14/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%A4%84%E7%90%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/07/14/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/%E5%9B%BE%E7%89%87%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%A4%84%E7%90%86/" class="post-title-link" itemprop="url">图片上传问题</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-07-14 21:28:16" itemprop="dateCreated datePublished" datetime="2021-07-14T21:28:16+08:00">2021-07-14</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:32:40" itemprop="dateModified" datetime="2021-11-03T16:32:40+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%AD%A6%E6%A0%A1%E9%A1%B9%E7%9B%AE%E5%BA%94%E7%94%A8/" itemprop="url" rel="index"><span itemprop="name">学校项目应用</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="图片上传问题"><a href="#图片上传问题" class="headerlink" title="图片上传问题"></a>图片上传问题</h1><p>正在参与的项目由于要求原因，所有的图片引入、存储均修改成base64的格式来进行，这里当时我负责图片下载部分的处理逻辑。上传图片时，用FormData的方式和表单一起来上传，由后端来分析并处理图片的base64格式，并存储在服务器静态资源路径下，数据库中保存存放地址路径。在前端获取图片时，则用axios发起ajax请求，异步获取base64格式的图片数据，再转换成图片显示，其实关键在于将base64转译成blob对象。</p>
<h2 id="文件格式类型"><a href="#文件格式类型" class="headerlink" title="文件格式类型"></a>文件格式类型</h2><p>Blob、ArrayBuffer、File可以归为一类，它们都是数据；FileReader算是一种工具，用来读取数据；FormData则可以看做是一个应用数据的场景。</p>
<h3 id="Blob"><a href="#Blob" class="headerlink" title="Blob"></a>Blob</h3><p>blob:全称是binary large object,二进制大文件对象，非Javascript特有，计算机通用对象。MDN上解释是，一个Blob对象就是一个包含有只读原始数据的类文件对象，通俗讲就是不可修改的二进制文件。一般我们通过构造函数创建Blob对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> aBlob = <span class="keyword">new</span> Blob( array, options );</span><br><span class="line"><span class="comment">/*array：一堆数据构成的数组 options：设置一些属性，主要是type属性	</span></span><br><span class="line"><span class="comment">Blob对象的属性:</span></span><br><span class="line"><span class="comment">size:数据的字节大小（只读）</span></span><br><span class="line"><span class="comment">type:数据的MIME类型，例如'image/jpeg'（只读）</span></span><br><span class="line"><span class="comment">(MIME:描述消息内容类型的因特网标准)</span></span><br><span class="line"><span class="comment">Blob对象只有slice方法，对文件分割</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">aBlob.slice(start, end ,contentType)</span><br></pre></td></tr></table></figure>

<h3 id="File"><a href="#File" class="headerlink" title="File"></a>File</h3><p>File:File就是文件，继承自Blob，也是二进制对象，也有自己特有的属性和方法，通常用在<input type="file">选择的FileList对象，或者是使用拖拽操作产生的的DataTransfer对象。下面我们用构造函数构建File对象：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myFile = <span class="keyword">new</span> File(array, name,options);</span><br><span class="line"><span class="comment">/* array：一堆数据构成的数组 </span></span><br><span class="line"><span class="comment">name:文件名</span></span><br><span class="line"><span class="comment">options：设置一些属性，type属性、lastModified</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">File对象属性</span></span><br><span class="line"><span class="comment">name：文件名</span></span><br><span class="line"><span class="comment">size：文件大小</span></span><br><span class="line"><span class="comment">lastModified ：最后修改时间（时间戳）</span></span><br><span class="line"><span class="comment">lastModifiedDate:最后修改时间Data对象</span></span><br><span class="line"><span class="comment">type：MIME类型</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">File对象方法</span></span><br><span class="line"><span class="comment">继承自Blob的slice方法</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">Blob.slice(start, end, contentType)</span><br></pre></td></tr></table></figure>

<h3 id="ArrayBuffer"><a href="#ArrayBuffer" class="headerlink" title="ArrayBuffer"></a>ArrayBuffer</h3><p>ArrayBuffer是内存上一段二进制数据，我们可以借助工具TypeArray、DataView对它进行读写。</p>
<p>Blob与ArrayBuffer的关系：</p>
<p>1、ArrayBuffer是底层二进制数据，可以借助工具进行读写，而Blob是对底层二进制数据的封装，我们拿到的就是一个整体，能够读取他的大小，类型，但是不能看到细节。</p>
<p>2、Blob可以接受一个ArrayBuffer作为参数生成一个Blob对象，此行为就相当于对ArrayBuffer数据做一个封装，之后就是以整体的形式展现了Blob作为一个整体文件，适合用于传输；而只有需要关注细节（比如要修改某一段数据时），才需要用到ArrayBuff。</p>
<h3 id="Base64"><a href="#Base64" class="headerlink" title="Base64"></a>Base64</h3><p>有些网络传送渠道并不支持所有的字节，例如传统的邮件只支持可见字符的传送，像ASCII码的控制字符就不能通过邮件传送。这样用途就受到了很大的限制，比如图片二进制流的每个字节不可能全部是可见字符，所以就传送不了。</p>
<p>最好的方法就是在不改变传统协议的情况下，做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示，问题就解决了。Base64编码应运而生，Base64就是一种基于64个可打印字符来表示二进制数据的表示方法。</p>
<h2 id="文件间相互转换"><a href="#文件间相互转换" class="headerlink" title="文件间相互转换"></a>文件间相互转换</h2><h3 id="File与Blob"><a href="#File与Blob" class="headerlink" title="File与Blob"></a>File与Blob</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// File文件转换为Blob对象;直接用new Blob新建对象即可，传入参数可为file数组。</span></span><br><span class="line"><span class="keyword">let</span> aBlob = <span class="keyword">new</span> Blob([file], &#123;<span class="attr">type</span>: file.type&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">//Blob转换为File；用new window.File新建即可，传入参数可谓blob；</span></span><br><span class="line"><span class="keyword">let</span> files = <span class="keyword">new</span> <span class="built_in">window</span>.File([<span class="keyword">this</span>.blob], file.name, &#123;<span class="attr">type</span>: file.type&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="File与Blob转换成base64"><a href="#File与Blob转换成base64" class="headerlink" title="File与Blob转换成base64"></a>File与Blob转换成base64</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//File/Blob文件转换为base64，利用FileReader对象来进行处理blob，可获取base64格式的结果。</span></span><br><span class="line"><span class="comment">// 原理：利用fileReader的readAsDataURL，将blob转为base64</span></span><br><span class="line"><span class="keyword">const</span> blobToBase64 = <span class="function">(<span class="params">blob</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> fileReader = <span class="keyword">new</span> FileReader();</span><br><span class="line">      fileReader.onload = <span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line">        resolve(e.target.result);</span><br><span class="line">      &#125;;</span><br><span class="line">      <span class="comment">// readAsDataURL</span></span><br><span class="line">      fileReader.readAsDataURL(blob);</span><br><span class="line">      fileReader.onerror = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'文件流异常'</span>));</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//可以如下调用，这种文件的转化，最好使用promise来写成异步的形式,毕竟文件的转换比较耗时</span></span><br><span class="line"><span class="keyword">this</span>.blobToBase64(blob).then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 转化后的base64</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'base64'</span>, res)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="图片url转base64"><a href="#图片url转base64" class="headerlink" title="图片url转base64"></a>图片url转base64</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原理： 利用canvas.toDataURL的API转化成base64</span></span><br><span class="line"><span class="keyword">const</span> urlToBase64 = <span class="function">(<span class="params">url</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span> (<span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> image = <span class="keyword">new</span> Image();</span><br><span class="line">        image.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">          <span class="keyword">let</span> canvas = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>);</span><br><span class="line">          canvas.width = <span class="keyword">this</span>.naturalWidth;</span><br><span class="line">          canvas.height = <span class="keyword">this</span>.naturalHeight;</span><br><span class="line">          <span class="comment">// 将图片插入画布并开始绘制</span></span><br><span class="line">          canvas.getContext(<span class="string">'2d'</span>).drawImage(image, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">          <span class="comment">// result</span></span><br><span class="line">          <span class="keyword">let</span> result = canvas.toDataURL(<span class="string">'image/png'</span>)</span><br><span class="line">          resolve(result);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">// CORS 策略，会存在跨域问题https://stackoverflow.com/questions/20424279/canvas-todataurl-securityerror</span></span><br><span class="line">        image.setAttribute(<span class="string">"crossOrigin"</span>,<span class="string">'Anonymous'</span>);</span><br><span class="line">        image.src = url;</span><br><span class="line">        <span class="comment">// 图片加载失败的错误处理</span></span><br><span class="line">        image.onerror = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">          reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'图片流异常'</span>));</span><br><span class="line">      &#125;;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="base64转blob与file"><a href="#base64转blob与file" class="headerlink" title="base64转blob与file"></a>base64转blob与file</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//base64 转 blob</span></span><br><span class="line"><span class="comment">// 原理：利用URL.createObjectURL为blob对象创建临时的URL</span></span><br><span class="line"><span class="keyword">const</span> base64ToBlob  = <span class="function">(<span class="params">&#123;b64data = <span class="string">''</span>, contentType = <span class="string">''</span>, sliceSize = <span class="number">512</span>&#125; = &#123;&#125;</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="comment">// 使用 atob() 方法将数据解码</span></span><br><span class="line">      <span class="keyword">let</span> byteCharacters = atob(b64data);</span><br><span class="line">      <span class="keyword">let</span> byteArrays = [];</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">let</span> offset = <span class="number">0</span>; offset &lt; byteCharacters.length; offset += sliceSize) &#123;</span><br><span class="line">        <span class="keyword">let</span> slice = byteCharacters.slice(offset, offset + sliceSize);</span><br><span class="line">        <span class="keyword">let</span> byteNumbers = [];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; slice.length; i++) &#123;</span><br><span class="line">            byteNumbers.push(slice.charCodeAt(i));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 8 位无符号整数值的类型化数组。内容将初始化为 0。</span></span><br><span class="line">        <span class="comment">// 如果无法分配请求数目的字节，则将引发异常。</span></span><br><span class="line">        byteArrays.push(<span class="keyword">new</span> <span class="built_in">Uint8Array</span>(byteNumbers));</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">let</span> result = <span class="keyword">new</span> Blob(byteArrays, &#123;</span><br><span class="line">        type: contentType</span><br><span class="line">      &#125;)</span><br><span class="line">      result = <span class="built_in">Object</span>.assign(result,&#123;</span><br><span class="line">        <span class="comment">// jartto: 这里一定要处理一下 URL.createObjectURL</span></span><br><span class="line">        preview: URL.createObjectURL(result),</span><br><span class="line">        name: <span class="string">`图片示例.png`</span></span><br><span class="line">      &#125;);</span><br><span class="line">      resolve(result)</span><br><span class="line">    &#125;)</span><br><span class="line"> &#125;;</span><br><span class="line"><span class="keyword">const</span> dataURLtoBlob = <span class="function"><span class="keyword">function</span> (<span class="params">dataurl</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> arr = dataurl.split(<span class="string">','</span>);</span><br><span class="line">    <span class="comment">//注意base64的最后面中括号和引号是不转译的</span></span><br><span class="line">    <span class="keyword">var</span> _arr = arr[<span class="number">1</span>].substring(<span class="number">0</span>,arr[<span class="number">1</span>].length<span class="number">-2</span>);</span><br><span class="line">    <span class="keyword">var</span> mime = arr[<span class="number">0</span>].match(<span class="regexp">/:(.*?);/</span>)[<span class="number">1</span>],</span><br><span class="line">        bstr =atob(_arr),<span class="comment">//获得解码后字符串</span></span><br><span class="line">        n = bstr.length,</span><br><span class="line">        u8arr = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(n);</span><br><span class="line">    <span class="comment">//正新建一个8位的整数类型数组，用于存放ASCII码编码的字符串</span></span><br><span class="line">    <span class="keyword">while</span> (n--) &#123;</span><br><span class="line">        u8arr[n] = bstr.charCodeAt(n);<span class="comment">//转换编码后。用charCodeAts找到Unicode编码</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Blob([u8arr], &#123;</span><br><span class="line">        type: mime</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">js中有两个方法专门用来处理base64编解码</span></span><br><span class="line"><span class="comment">window.btoa() 和 window.atob()</span></span><br><span class="line"><span class="comment">btoa() 方法使用base-64 编码字符串</span></span><br><span class="line"><span class="comment">atob() 方法用于解码使用 base-64 编码的字符串。</span></span><br><span class="line"><span class="comment">注意⚠️：这两个方法都是返回字符串 atob()方法解码后返回的是unicode编码的字符串 不是二进制对象</span></span><br><span class="line"><span class="comment">上面该函数将base64编码转换成blob对象后，后续再利用</span></span><br><span class="line"><span class="comment">window.url.createObjectURL来进行构建DOM对象。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">let</span> newBlob = <span class="built_in">window</span>.URL.createObjectURL(dataURLtoBlob(base64));</span><br></pre></td></tr></table></figure>

<h3 id="FileReader"><a href="#FileReader" class="headerlink" title="FileReader"></a>FileReader</h3><p>通过fileReader可以将Blob、File读取为不同的格式。</p>
<p>FileReader.readAsDataURL(blob)将二进制数据读取并编码为Base64格式</p>
<p>FileReader.readAsText(blob)就是将二进制数据读取并编码为字符串形式</p>
<h3 id="FormData"><a href="#FormData" class="headerlink" title="FormData"></a>FormData</h3><p>使用FormData我们可以异步上传一个二进制文件，而这个二进制文件，就是我们上面讲的Blob对象。</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/page/4/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/4/">4</a><span class="page-number current">5</span><a class="page-number" href="/page/6/">6</a><span class="space">&hellip;</span><a class="page-number" href="/page/10/">10</a><a class="extend next" rel="next" href="/page/6/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

